GitHub repository: bokeh/bokeh

Issues:
issue_13727: state: open
title: [BUG] Javascript Error Using AjaxDataSource With Image Data
body: ### Software versions

Python version        :  3.12.2 (main, Feb 25 2024, 16:36:57) [GCC 9.4.0]
IPython version       :  (not installed)
Tornado version       :  6.4
Bokeh version         :  3.3.4
BokehJS static path   :  /home/zuber/dev/bokeh_bug/venv312/lib/python3.12/site-packages/bokeh/server/static
node.js version       :  (not installed)
npm version           :  (not installed)
jupyter_bokeh version :  (not installed)
Operating system      :  Linux-5.4.0-172-generic-x86_64-with-glibc2.31


### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

I am attempting to use an AjaxDataSource to drive dynamic image data as a 2 dimensional data display using Bokeh and Flask. This worked using nested arrays under Bokeh 2.4.3, but does not with the serialized data transfers which are now required under 3.3.4. When I send `numpy.ndarray` data using the `bokeh.core.serialization.Serializer` object I'd expect the image data to render as expected.

### Observed behavior

When the data is sent, I get an error on the javascript side that is of the form:
```
Uncaught (in promise) Error: AjaxDataSource(p1042).data given invalid value: {"type":"map","entries":[["image",[{"type":"ndarray","array":{"type":"bytes","data":"5u4Riq8V2j9KUi49teHpPxDOBrtuje...
```

### Example code

Python
The following was run in a fresh python 3.12 virtual environment. The following packages need to be installed with pip prior to the run:
`pip install numpy bokeh flask flask_restful`

To run, from the venv run `python main.py`. Then open your browser to http://localhost:8000/

The old behavior can be observed by installing
`pip install bokeh==2.4.3`
and then commenting/uncommenting out the lines as denoted in the file.

<demo.html.jinja>
```
div class="row" style="height: 600px">
    <div class="col">
        {{ div | safe }}
    </div>
</div>

{% for bokeh_static_resource in bokeh_static_js.js_raw %}
    <script>{{ bokeh_static_resource | safe }}</script>
{% endfor %}

{{ script | safe }}
```

<main.py>
```
# Run
# pip install numpy bokeh flask flask_restful
# before running this test demo
import numpy as np

# Start Bokeh 3.3.4 (comment out for 2.4.3)
from bokeh.core.json_encoder import serialize_json
from bokeh.core.serialization import Serializer
# End Bokeh 3.3.4

from bokeh.resources import INLINE
from bokeh.embed import components
from bokeh.models import (
    AjaxDataSource,
    LinearColorMapper,
    Range1d,
)
from bokeh.models.annotations import ColorBar
from bokeh.models.callbacks import CustomJS
from bokeh.plotting import figure

from flask import (
    Blueprint,
    jsonify,
    url_for,
    make_response,
    render_template
)
from flask import Flask

from flask_restful import Api, Resource

NUM_X_CELLS = 64
NUM_Y_CELLS = 64

DEFAULT_X_MIN = 0
DEFAULT_Y_MIN = 0
DEFAULT_X_MAX = NUM_X_CELLS
DEFAULT_Y_MAX = NUM_Y_CELLS

DEFAULT_COLOR_HIGH = 1.0
DEFAULT_COLOR_LOW = 0.0


class LiveDisplay(Resource):

    def get(self):
        """Get the Display page."""
        plot = figure(
            tools="reset, wheel_zoom, pan, box_zoom",
            x_range=Range1d(DEFAULT_X_MIN, DEFAULT_X_MAX, bounds="auto"),
            y_range=Range1d(DEFAULT_Y_MIN, DEFAULT_Y_MAX, bounds="auto"),
            sizing_mode="stretch_both",
            title="Waiting for data...",
            background_fill_color="white",
            x_axis_label="Waiting on data...",
            y_axis_label="Waiting on data...",
        )

        source = AjaxDataSource(
            data_url=url_for('testdisplay.imagedata'),
            polling_interval=500,
            method="GET",
            mode="replace",
            syncable=False,
        )

        color_mapper = LinearColorMapper(
            palette="Turbo256",
            low=DEFAULT_COLOR_LOW,
            high=DEFAULT_COLOR_HIGH
        )

        plot.image(
            source=source,
            x="image_x",
            y="image_y",
            color_mapper=color_mapper,
        )

        color_bar = ColorBar(
            title="Waiting For Data",
            color_mapper=color_mapper,
            label_standoff=12
        )

        source.js_on_change(
            "data",
            CustomJS(
                args={
                    "title": plot.title,
                    "x_label": plot.xaxis[0],
                    "y_label": plot.yaxis[0],
                    "source": source,
                    "color_bar": color_bar,
                },
                code=(
                    "title.text = source.data.title[0];"
                    "x_label.axis_label = source.data.x_label[0];"
                    "y_label.axis_label = source.data.y_label[0];"
                    "color_bar.title = source.data.z_label[0];"
                )
            )
        )

        plot.add_layout(color_bar, 'right')

        script, div = components(plot)

        return make_response(
            render_template(
                'demo.html.jinja',
                script=script,
                div=div,
                bokeh_static_js=INLINE
            ),
            200,
            {'Content-Type': 'text/html'}
        )


class ImageData(Resource):
    """
    Intended to drive AJAX calls from Bokeh to get updated data.
    """
    def get(self):
        """Get handler for returning data."""
        disp_values = np.random.rand(NUM_X_CELLS, NUM_Y_CELLS)

        plot_title = "hello world"
        x_label = f"foo"
        y_label = f"bar"
        z_label = f"baz"

        data = {
            #'image': [disp_values.tolist()],  # Bokeh 2.4.3 (comment out for 3.3.4)
            'image': [disp_values],  # Bokeh 3.3.4 (comment out for 2.4.3)
            'image_x': [0],
            'image_y': [0],
            'dw': [NUM_X_CELLS],
            'dh': [NUM_Y_CELLS],
            'title': [plot_title],
            'x_label': [x_label],
            'y_label': [y_label],
            'z_label': [z_label],
        }

        # response = jsonify(data)  # Bokeh 2.4.3 (comment out for 3.3.4)

        # Start Bokeh 3.3.4 (comment out for 2.4.3)
        # Note I would also be interested if there was a more standard way of doing this.
        bokeh_serializer = Serializer()

        encoded = bokeh_serializer.encode(data)
        bokeh_json = serialize_json(encoded)

        response = make_response()

        response.response = bokeh_json
        # End Bokeh 3.3.4

        response.add_etag()
        response.cache_control.no_cache = True

        return response



blueprint = Blueprint(
    'testdisplay',
    __name__,
    template_folder='',
)

app = Flask(__name__)

api = Api(blueprint)

api.add_resource(LiveDisplay, '/')
api.add_resource(ImageData, '/data/')

app.register_blueprint(blueprint)


def run():
    """Run the display locally."""
    app.run(
        host="localhost",
        port="8000",
        debug=True
    )


if __name__ == '__main__':
    run()
```

### Closing Remarks
I posted on the [discourse site](https://discourse.bokeh.org/t/javascript-error-using-ajaxdatasource-with-image-data/11306) as a sanity check before filing this bug and got a few suggestions from @bryevdv.  Unfortunately these did not seem to change the situation.  He suggested I file this bug report and bring it to the attention of @mattpap as well.

Thanks for your efforts on this awesome project!
user: rez10191
issue_13725: state: open
title: [BUG] Enforce `max_interval` / `min_interval` if `{x,y}_range` is not set 
body: ### Software versions

``` yaml
Python version        :  3.12.2 | packaged by conda-forge | (main, Feb 16 2024, 20:50:58) [GCC 12.3.0]
IPython version       :  8.22.1
Tornado version       :  6.4
Bokeh version         :  3.4.0.dev8
BokehJS static path   :  /home/shh/miniconda3/envs/holoviz/lib/python3.12/site-packages/bokeh/server/static
node.js version       :  v20.9.0
npm version           :  10.1.0
jupyter_bokeh version :  4.0.0
Operating system      :  Linux-6.6.10-76060610-generic-x86_64-with-glibc2.35
```

### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

`max_interval` and `min_interval` was added in https://github.com/bokeh/bokeh/pull/13561. If no `{x,y}_range` is set, it should not exceed `max_interval`. 

I'm not sure if this is a bug report or a feature request.

### Observed behavior

The initial render has the full range of the plots, and when you move it, it snaps to the `max_interval` size. 

### Example code

```Python
import numpy as np
from bokeh.layouts import column
from bokeh.models import ColumnDataSource, RangeTool
from bokeh.plotting import figure, show


n_points = 3000
x_values = np.linspace(0, 100, n_points)
y_values = np.random.randn(n_points).cumsum()

source = ColumnDataSource(data=dict(x=x_values, y=y_values))

p = figure(
    height=300,
    width=800,
    tools=[" xpan", "xzoom_in", "xzoom_out", "reset"],
    background_fill_color="#efefef",
    # x_range=(20, 30),
)
p.line("x", "y", source=source)
p.yaxis.axis_label = "Value"
p.x_range.max_interval = 10  # Set max zoom interval

select = figure(
    height=130,
    width=800,
    y_range=p.y_range,
    tools="",
    toolbar_location=None,
    background_fill_color="#efefef",
)
range_tool = RangeTool(x_range=p.x_range)
range_tool.overlay.fill_color = "navy"
range_tool.overlay.fill_alpha = 0.2
select.line("x", "y", source=source)
select.ygrid.grid_line_color = None
select.add_tools(range_tool)

show(column(p, select))
```


### Stack traceback or browser console output

_No response_

### Screenshots


https://github.com/bokeh/bokeh/assets/19758978/1716ac2e-7523-4f17-90fe-15b7bf0957db


user: Hoxbro
issue_13720: state: open
title: Explanation is part of the code block in the latex_axis_labels_titles_labels example
body: In the [latex on axis and titles](https://docs.bokeh.org/en/latest/docs/examples/styling/mathtext/latex_axis_labels_titles_labels.html) example the code block is not pure python code.

![grafik](https://github.com/bokeh/bokeh/assets/68053396/76c2d512-8c47-45a8-ac87-354942e105b5)

It also displays the information which should be evaluated by the `bokeh-plot` directive.
user: mosc9575
issue_13712: state: open
title: [BUG] max_zoom+1 is used for WMTSTileSource/MercatorTileSource
body: ### Software versions

```
Python version        :  3.11.5 | packaged by conda-forge | (main, Aug 27 2023, 03:33:12) [Clang 15.0.7 ]
IPython version       :  8.15.0
Tornado version       :  6.3.3
Bokeh version         :  3.3.1
BokehJS static path   :  /Users/bane.sullivan/mambaforge/envs/geospatial/lib/python3.11/site-packages/bokeh/server/static
node.js version       :  v21.1.0
npm version           :  10.2.0
jupyter_bokeh version :  (not installed)
Operating system      :  macOS-14.2.1-arm64-arm-64bit
```

### Browser name and version

Chrome Version 121.0.6167.184 (Official Build) (arm64)

### Jupyter notebook / Jupyter Lab version

JupyterLab 4.0.5

### Expected behavior

When I specify a `max_zoom` level of 16 to `WMTSTileSource`, expect no further tile requests pas zoom level 16 when continuing to zoom.

### Observed behavior

When I specify a `max_zoom` level of 16 (`z`) to `WMTSTileSource`, I observe request for tiles stopping at zoom level 17 (`z+1`)

I'm curious why this happens as it seems counter intuitive to me and is different behavior from other plotting libraries like Leaflet.js


I think I've tracked this down to the following line:

https://github.com/bokeh/bokeh/blob/f261761b7c3dfbaa39cfeed1b3ea4295980713e5/bokehjs/src/lib/models/tiles/mercator_tile_source.ts#L42

### Example code

```Python
from bokeh.plotting import figure, show
from bokeh.models import WMTSTileSource

provider = WMTSTileSource(
        url='https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{Z}/{Y}/{X}.jpg',
        max_zoom=3,
    )
# Fo some reason, I see requests for max_zoom+1 tiles?

p = figure(x_range=(-2000000, 2000000), y_range=(1000000, 7000000),
           x_axis_type="mercator", y_axis_type="mercator")
p.add_tile(provider, retina=True)
show(p)
```


### Stack traceback or browser console output

While normally this likely isn't a problem as many WM tile services will yield a 404 for zoom levels that don't exist, some tile services will continue to issue 200 response codes for non-existent zoom levels but with a placeholder image like:

![307244732-ed387371-6cf7-4116-8fb1-26864ff906a5](https://github.com/bokeh/bokeh/assets/144726278/4e2885fb-9dc5-424c-95fb-bcbec0fbd618)

We can see this in action with the above example and changing the zoom level to 16 (the max for the `https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{Z}/{Y}/{X}.jpg` tile service) and then see Bokeh request tiles for zoom level 17 which is passed the specified `max_zoom` and yeilds the placeholder image from this service.

### Screenshots

Specifying `max_zoom=3` but seeing requests in the browser's network tab for zoom level 4:

<img width="1362" alt="Screenshot 2024-02-23 at 8 36 32 AM" src="https://github.com/bokeh/bokeh/assets/144726278/25c67194-8835-46d4-9142-ae4158ae3472">

user: banesullivan-kobold
issue_13708: state: open
title: [FEATURE] Support log axes for "pre-transformed" data
body: ### Problem description

The hex tiles plot assumes linear axes only

https://github.com/bokeh/bokeh/blob/b627a152c21b6484de867db41b8163a74ebb9566/bokehjs/src/lib/models/glyphs/hex_tile.ts#L116-L117

### Feature description

It would be awesome to support log scale axes as well for the hex binning/tiles plot

### Potential alternatives

N/A?

### Additional information

Originally reported in https://github.com/holoviz/holoviews/issues/6117

```py
import numpy as np
import holoviews as hv
from holoviews import opts
hv.extension('bokeh')

np.random.seed(44)
data = np.exp(np.random.randn(100000, 2) + 20)

hv.HexTiles(data).opts(logx=True, logy=True)
```
![304894728-f6e7bc6b-6d46-4745-b4db-f1a04ed93adb](https://github.com/bokeh/bokeh/assets/144726278/df473102-1e8d-4402-950a-6438aabdf9c2)


user: banesullivan-kobold
issue_13706: state: open
title: Minify GLSL shaders
body: Fixes #13335.

This minifies the WebGL shaders, mostly by removing comments and unnecessary whitespace. Example of shader before minifying:
```glsl
precision mediump float;

attribute vec2 a_position;
attribute vec4 a_bounds;

uniform float u_pixel_ratio;
uniform vec2 u_canvas_size;

varying vec2 v_tex_coords;

void main()
{
  v_tex_coords = vec2(a_position.x < 0.0 ? 0.0 : 1.0, a_position.y < 0.0 ? 0.0 : 1.0);

  float x = a_position.x < 0.0 ? a_bounds[0] : a_bounds[2];
  float y = a_position.y < 0.0 ? a_bounds[1] : a_bounds[3];
  vec2 xy = vec2(x, y);

  vec2 pos = xy + 0.5;  // Bokeh's offset.
  pos /= u_canvas_size / u_pixel_ratio;  // in 0..1
  gl_Position = vec4(2.0*pos.x - 1.0, 1.0 - 2.0*pos.y, 0.0, 1.0);
}
```
and after:
```glsl
precision mediump float;attribute vec2 a_position;attribute vec4 a_bounds;uniform float u_pixel_ratio;uniform vec2 u_canvas_size;varying vec2 v_tex_coords;void main(){v_tex_coords=vec2(a_position.x<0.?0.:1.,a_position.y<0.?0.:1.);float x=a_position.x<0.?a_bounds[0]:a_bounds[2];float y=a_position.y<0.?a_bounds[1]:a_bounds[3];vec2 xy=vec2(x,y);vec2 pos=xy+.5;pos/=u_canvas_size/u_pixel_ratio;gl_Position=vec4(2.*pos.x-1.,1.-2.*pos.y,0.,1.);}
```
Bundle sizes on `branch-3.4`:
```
    - bokeh-gl.js          :  551.5 KB
    - bokeh-gl.min.js      :  204.7 KB
```
and after this PR:
```
    - bokeh-gl.js          :  551.5 KB
    - bokeh-gl.min.js      :  186.9 KB
```
so it saves 17.8 KB of the `gl-min` bundles, about 8.7%.

I looked at existing NPM packages for this and didn't find any I liked. Mostly they are unmaintained and tightly coupled to webpack. So I wrote my own as a sequence of regex replacements. These should by understandable by anyone (including my future self) with some regex knowledge. The multiple passes of each shader, one per regex, are probably not very quick but GLSL is such a small fraction of the BokehJS codebase that there isn't any measurable increase in build time on my dev machine.

In future we could extend the minifying to replace local function and variable names with smaller strings, but that would require storing state so would need a different implementation.

Testing is a problem. I think that the visual baseline tests use the unminified bundles so this will not be tested by CI. Could you confirm this @mattpap?
user: ianthomas23
issue_13705: state: open
title: [BUG] Slight change in axes when select using match_aspect
body: ### Software versions

Python version        :  3.11.7 | packaged by conda-forge | (main, Dec 23 2023, 14:43:09) [GCC 12.3.0]
IPython version       :  8.20.0
Tornado version       :  6.3.3
Bokeh version         :  3.4.0.dev8+6.g0021c18b
BokehJS static path   :  /home/iant/github/bokeh/src/bokeh/server/static
node.js version       :  v18.13.0
npm version           :  10.1.0
jupyter_bokeh version :  (not installed)
Operating system      :  Linux-6.5.0-17-generic-x86_64-with-glibc2.38

### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

When you tap select a glyph the axes positions should not change.

### Observed behavior

Axes positions change slightly but only if `match_aspect=True`. 

### Example code

```Python
from bokeh.plotting import figure, show

p = figure(width=400, height=400, match_aspect=True,
           tools="tap,reset")
p.rect(x=[0, 1, 0, 1], y=[0, 0, 1, 1], width=0.8, height=0.8)
show(p)
```


### Stack traceback or browser console output

_No response_

### Screenshots

Before and after tap select:
![before](https://github.com/bokeh/bokeh/assets/580326/84bf1b1a-5b8e-4957-b10e-3a2294d48f9c)
![after](https://github.com/bokeh/bokeh/assets/580326/e792f89e-e63e-4576-967b-7dda3713378c)

user: ianthomas23
issue_13699: state: open
title: [BUG] Toolbar above plot with many hovers shifted to the right
body: ### Software versions

Python version        :  3.12.1 | packaged by conda-forge | (main, Dec 23 2023, 07:53:56) [MSC v.1937 64 bit (AMD64)]
IPython version       :  8.21.0
Tornado version       :  6.3.3
Bokeh version         :  3.3.4
BokehJS static path   :  C:\Users\XXXX\miniconda3\envs\myenv312\Lib\site-packages\bokeh\server\static
node.js version       :  (not installed)
npm version           :  (not installed)
jupyter_bokeh version :  (not installed)
Operating system      :  Windows-10-10.0.19045-SP0

### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

I have a plot with many curves.
Since I want to have line specific hover labels, I created one `HoverTool` per line, using `toggleable=False` because I don't need them to be toggled and shown in the toolbar.
Because I have a legend right outside of the plot in my real case and I find that the toolbar right of it doesn't look nice (and I don't have so much room), I placed the toolbar on the top.
I would expect following output:
![Expectation](https://github.com/bokeh/bokeh/assets/94801369/8c1c1bac-5574-48ef-a5c9-7421e270f919)


### Observed behavior

But I observe that the toolbar is shifted to the right, possibly overlapping with other objects:
![Error case](https://github.com/bokeh/bokeh/assets/94801369/4696c5a3-e080-4dde-b39d-999b1a0e3bd2)


### Example code

```Python
import random
from bokeh.plotting import figure, show
from bokeh.models import ColumnDataSource, HoverTool, Div
from bokeh.layouts import row, Spacer
from bokeh.palettes import Category10

# Define the number of lines:
line_nb = 15

# Prepare some data:
source_dict = {"x": list(range(1, 6))}
for i in range(line_nb):
    source_dict[f"y{i}"] = [random.random() for j in source_dict["x"]]

source = ColumnDataSource(data=source_dict)

# Create a new plot:
p = figure(title="Multiline graph", x_axis_label="x", y_axis_label="y", height=300, toolbar_location="above")
# Workaround: toolbar_sticky=False but takes more vertical space.
# Other toolbar_location "below", "right", "left" behave as expected.

# Add renderers and hovers:
for n in range(line_nb):
    y_name = list(source_dict.keys())[n+1]
    renderer = p.line(source=source,
           x=list(source_dict.keys())[0],
           y=y_name,
           color=Category10[10][n%10],
           )
    
    hover = HoverTool(toggleable=False) # <-- Changing to toggleable=True shows all buttons and explains the current position of the toolbar.
    hover.tooltips = [("(x, "+y_name+")", "(@{x}, @{"+y_name+"})")] # With line specific labels
    hover.renderers = [renderer]
    p.add_tools(hover)

# Setup and show layout:
div = Div(text="Some other graphical element")
layout = row(p, Spacer(width=50), div)
show(layout)
```


### Stack traceback or browser console output

The right end of the toolbar is exactly at the same position as it would be if the toggle buttons were displayed (hover setting `toggleable=True`):
![Toggleable=True](https://github.com/bokeh/bokeh/assets/94801369/479124e0-86d1-4179-9977-fb4b2b3dfd24)
Also with `toggleable=True`, the automatic hiding of exceeding toggle buttons does not work as it would if I place the toolbar at the bottom of the plot:
![Below red](https://github.com/bokeh/bokeh/assets/94801369/b5421b27-7eb8-4838-bc0e-408d0015f671)

**Workaround**
My workaround is to use `toolbar_sticky=False` in the figure definition. This works, but this shifts the toolbar upwards further away from the plot area, so this is not so nice.
In my case, I have several plots in a column, and it then looks like the toolbar of a lower plot actually belongs to the one above.
![Workaround](https://github.com/bokeh/bokeh/assets/94801369/d6123261-39fe-4f2a-ac6f-bc9ad5c9fd4d)


### Screenshots

_No response_
user: Icoti
issue_13692: state: open
title: [BUG] Blurry plots with WebGL
body: ### Software versions

```
Python version        :  3.11.5 | packaged by conda-forge | (main, Aug 27 2023, 03:33:12) [Clang 15.0.7 ]
IPython version       :  8.15.0
Tornado version       :  6.3.3
Bokeh version         :  3.3.4
BokehJS static path   :  <MY CONDA ENV DIR>/lib/python3.11/site-packages/bokeh/server/static
node.js version       :  v21.1.0
npm version           :  10.2.0
jupyter_bokeh version :  (not installed)
Operating system      :  macOS-12.5-arm64-arm-64bit
```

### Browser name and version

Firefox 122.0.1

### Jupyter notebook / Jupyter Lab version

JupyterLab 4.1.1

### Expected behavior

The second plot should not be blurry

### Observed behavior

The second plot should is blurry

### Example code

```Python
import bokeh.plotting
import bokeh.palettes
import bokeh.io
import holoviews as hv

bokeh.io.output_notebook()
hv.extension('bokeh')

x = [0, 1, 2, 3]
y = [1, 2, 3, 2]
width = 500
height = 300
color = bokeh.palettes.Set1_3[1]

p1 = bokeh.plotting.figure(width=width, height=height)
p1.line(x, y, width=2, color=color)
bokeh.plotting.show(p1)

p2 = bokeh.plotting.figure(width=width, height=height, output_backend='webgl')
p2.line(x, y, width=2, color=color)
bokeh.plotting.show(p2)
```


### Stack traceback or browser console output

_No response_

### Screenshots

The first plot:

![0](https://github.com/bokeh/bokeh/assets/8861742/a97e9718-dfa7-474b-833d-f1e2331664b8)

The second plot:

![1](https://github.com/bokeh/bokeh/assets/8861742/2a889716-1548-4413-9cdf-4ae4ca3b5d41)

user: Yura52
issue_13691: state: open
title: [BUG] Most toolbar functions non-functional after update
body: ### Software versions

OS: Linux Mint 20
Python: 3.10.9 (Anaconda 2023.03-1) and 3.11.5 (Anaconda 2023.09-0)
Bokeh: 2.4.3 and 3.2.1

### Browser name and version

Chrome 121.0.6167.160 and Firefox 122.0.1

### Jupyter notebook / Jupyter Lab version

3.5.3 and 3.6.3

### Expected behavior

I created a simple line plot and found that only scroll zoom and refresh work in the plots. I can select other functions from the toolbar, but with no effect. I also tried adding a lasso select, and that didn't work either.

I tested in the two Anaconda distros noted above, with only the stock configuration and package versions. No post-install modifications. With Anaconda 2023.03-1 everything works as expected, but with Anaconda 2023.09-0 I experience the issue described.

I have also tried this with several version combinations of bokeh and jupyter-lab, including the latest (3.3.4 and 4.1.0 respectively) in a virtual environment, and none of them worked.

I have posted this on [discourse](https://discourse.bokeh.org/t/several-toolbar-buttons-nonfunctional-in-jupyter-notebook/11251), and was advised to submit an issue report.

### Observed behavior

Toolbar buttons can be selected, but to no effect. 

### Example code

```Python
# In[1]:

import numpy as np
import scipy as sp
from bokeh.plotting import figure, show
from bokeh.io import output_notebook, push_notebook
from bokeh.models import LassoSelectTool

# In[2]:

fs, data = sp.io.wavfile.read('/usr/share/sounds/alsa/Front_Right.wav')
t = np.linspace(0, 1, len(data))
carrier = np.sin(2*np.pi*200E3*t)

# In[3]:

output_notebook()

# In[4]:

p = figure()
p.line(np.linspace(0, 1, len(data)), data)

# In[5]:

show(p)

# In[6]:

data.shape

# In[7]:

X = np.fft.rfft(data)
f = np.fft.fftfreq(len(X)) * 48000

# In[8]:

p2 = figure()
p2.line(f, np.abs(X))

# In[9]:

show(p2)

# In[ ]:
```


### Stack traceback or browser console output

Console output:

```
remoteEntry.66500334d7b36213676e.js:1 Unsatisfied version 6.0.2 of shared singleton module @jupyter-widgets/base (required ^2.0.0 || ^3.0.0 || ^4.0.0)
s @ remoteEntry.66500334d7b36213676e.js:1
api/me?1707754618378:1 
        
        
       Failed to load resource: the server responded with a status of 404 (Not Found)
jlab_core.4d5114e25256eeddf4e4.js?v=4d5114e25256eeddf4e4:2 Language pack 'en_US' not installed!
fetch @ jlab_core.4d5114e25256eeddf4e4.js?v=4d5114e25256eeddf4e4:2
150.b0e841b75317744a7595.js?v=b0e841b75317744a7595:1 Failed to fetch ipywidgets through the "jupyter.widget.control" comm channel, fallback to fetching individual model state. Reason: Control comm was closed too early
_loadFromKernel @ 150.b0e841b75317744a7595.js?v=b0e841b75317744a7595:1
bokeh-3.2.1.min.js:183 [bokeh] setting log level to: 'info'
bokeh-3.2.1.min.js:165 [bokeh] document idle at 211 ms
bokeh-3.2.1.min.js:165 [bokeh] document idle at 87 ms
```

### Screenshots

_No response_
user: orodbhen
issue_13686: state: open
title: added metadata to spans and strips example
body: - [x] issues: fixes #11765 
- [x] tests added / passed

user: AkintolaRichard
issue_13680: state: open
title: Remove year or year range from copyright notices
body: I recently learned that updating copyright years is unnecessary: https://hynek.me/til/copyright-years/ . I've removed the copyright years from a number of my projects. Cheers to fewer chores 🎉

_Originally posted by @sloria in https://github.com/bokeh/bokeh/issues/13630#issuecomment-1922443533_
            
user: mattpap
labels: tag: codebase: Improvements to code quality without affecting any functionality.
issue_13676: state: open
title: [FEATURE] Sticky scroll for Tabs
body: ### Problem description

Tab titles disappeared while scrolling

### Feature description

Fix tab titles while scrolling, like this one, https://www.w3schools.com/howto/howto_css_fixed_menu.asp

![image](https://github.com/bokeh/bokeh/assets/102862066/93b00e85-00ed-4ea9-bc29-28ea56b5b327)

![image](https://github.com/bokeh/bokeh/assets/102862066/b655bd58-8339-4abc-a0de-4496ec17f2e4)


### Potential alternatives

Achieve this by custom css file. Or put this Tabs widget inside a box and fix titles in Tabs

### Additional information

I searched for "sticky scroll", "fix position", "ScrollBox", found no solution.
user: m3lab-zzl
labels: tag: CSS: CSS, styling, theming and widget design.
issue_13675: state: open
title: Add carbon theme
body: <img width="699" alt="image" src="https://github.com/bokeh/bokeh/assets/15331990/7da695f5-1597-46e1-bceb-abf6def24fd1">

Based on https://medium.com/@pYdeas/create-publication-styled-figures-with-hvplot-using-the-bokeh-backend-b623ab48a5d2

- [x] issues: fixes #13673
- [ ] tests added / passed
- [ ] release document entry (if new feature or API change)


Tested thru holoviews:
```python
import pandas as pd
import hvplot.pandas
import holoviews as hv
from bokeh.themes import Theme

json = {
    "attrs": {
        "figure": {
            "background_fill_color": "#1b1e23",
            "border_fill_color": "#1b1e23",
            "outline_line_alpha": 0,
        },
        "Grid": {
            "grid_line_color": "#808080",
            "grid_line_alpha": 0.1,
        },
        "Axis": {
            # tick color and alpha
            "major_tick_line_color": "#4d4f51",
            "minor_tick_line_alpha": 0,
            # tick labels
            "major_label_text_font": "Courier New",
            "major_label_text_color": "#808080",
            "major_label_text_align": "left",
            "major_label_text_font_size": "0.95em",
            "major_label_text_font_style": "normal",
            # axis labels
            "axis_label_text_font": "Courier New",
            "axis_label_text_font_style": "normal",
            "axis_label_text_font_size": "1.15em",
            "axis_label_text_color": "lightgrey",
            "axis_line_color": "#4d4f51",
        },
        "Legend": {
            "spacing": 8,
            "glyph_width": 15,
            "label_standoff": 8,
            "label_text_color": "#808080",
            "label_text_font": "Courier New",
            "label_text_font_size": "0.95em",
            "label_text_font_style": "bold",
            "border_line_alpha": 0,
            "background_fill_alpha": 0,
            "inactive_fill_alpha": 0,
        },
        "BaseColorBar": {
            # axis labels
            "title_text_color": "lightgrey",
            "title_text_font": "Courier New",
            "title_text_font_size": "0.95em",
            "title_text_font_style": "normal",
            # tick labels
            "major_label_text_color": "#808080",
            "major_label_text_font": "Courier New",
            "major_label_text_font_size": "0.95em",
            "major_label_text_font_style": "normal",
            "background_fill_color": "#1b1e23",
            "major_tick_line_alpha": 0,
            "bar_line_alpha": 0,
        },
        "Title": {
            "text_font": "Courier New",
            "text_font_style": "normal",
            "text_color": "lightgrey",
        },
    }
}

theme = Theme(json=json)

hv.renderer("bokeh").theme = theme

# load in data and postprocess input
df = (
    pd.read_csv(
        "https://mesonet.agron.iastate.edu/cgi-bin/request/daily.py?network=WA_ASOS&stations=SEA&year1=1928&month1=1&day1=1&year2=2023&month2=12&day2=31&var=max_temp_f&var=min_temp_f&var=precip_in&na=blank&format=csv",
        parse_dates=True,
        index_col="day",
    )
    .drop(columns=["station"])
    .astype("float16")
    .assign(
        dayofyear=lambda df: df.index.dayofyear,
        year=lambda df: df.index.year,
    )
)

# get average and 2023
df_avg = df.loc[df["year"].between(1990, 2020)].groupby("dayofyear").mean()
df_2023 = df[df.year == 2023]

# preprocess below/above
df_above = df_2023[["dayofyear", "max_temp_f"]].merge(
    df_avg.reset_index()[["dayofyear", "max_temp_f"]],
    on="dayofyear",
    suffixes=("_2023", "_avg"),
)
df_above["max_temp_f"] = df_above["max_temp_f_avg"]
df_above["max_temp_f"] = df_above.loc[
    df_above["max_temp_f_2023"] >= df_above["max_temp_f_avg"], "max_temp_f_2023"
]

df_below = df_2023[["dayofyear", "max_temp_f"]].merge(
    df_avg.reset_index()[["dayofyear", "max_temp_f"]],
    on="dayofyear",
    suffixes=("_2023", "_avg"),
)
df_below["max_temp_f"] = df_below["max_temp_f_avg"]
df_below["max_temp_f"] = df_below.loc[
    df_below["max_temp_f_2023"] < df_below["max_temp_f_avg"], "max_temp_f_2023"
]

days_above = df_above.query("max_temp_f_2023 >= max_temp_f_avg")["max_temp_f"].size
days_below = df_below.query("max_temp_f_2023 < max_temp_f_avg")["max_temp_f"].size

# create plot elements
plot = df.hvplot(
    x="dayofyear",
    y="max_temp_f",
    by="year",
    color="grey",
    alpha=0.02,
    legend=False,
    hover=False,
)
plot_avg = df_avg.hvplot(x="dayofyear", y="max_temp_f", color="grey", legend=False)
plot_2023 = df_2023.hvplot(x="dayofyear", y="max_temp_f", color="black", legend=False)
scatter_2023 = df_2023.hvplot.scatter(
    x="dayofyear",
    y="max_temp_f",
    color="max_temp_f",
    size=5,
    legend=False,
    cmap="RdBu_r",
    alpha=0.25,
    clabel="TEMPERATURE °F",
)

dark_red = "#FF5555"
dark_blue = "#5588FF"

plot_above = df_above.hvplot.area(
    x="dayofyear", y="max_temp_f_avg", y2="max_temp_f", label="above", legend="top_right"
).opts(fill_alpha=0.2, line_alpha=0.8, line_color=dark_red, fill_color=dark_red)
plot_below = df_below.hvplot.area(
    x="dayofyear", y="max_temp_f_avg", y2="max_temp_f", label="below", legend="top_right"
).opts(fill_alpha=0.2, line_alpha=0.8, line_color=dark_blue, fill_color=dark_blue)

text_days_above = hv.Text(
    35, df_2023["max_temp_f"].max(), f"{days_above}", fontsize=14
).opts(text_align="right", text_baseline="bottom", text_color=dark_red, text_alpha=0.8)
text_days_below = hv.Text(
    35, df_2023["max_temp_f"].max(), f"{days_below}", fontsize=14
).opts(text_align="right", text_baseline="top", text_color=dark_blue, text_alpha=0.8)
text_above = hv.Text(38, df_2023["max_temp_f"].max(), "DAYS ABOVE", fontsize=7).opts(
    text_align="left", text_baseline="bottom", text_color="lightgrey", text_alpha=0.8
)
text_below = hv.Text(38, df_2023["max_temp_f"].max(), "DAYS BELOW", fontsize=7).opts(
    text_align="left", text_baseline="above", text_color="lightgrey", text_alpha=0.8
)

# overlay every ing and save
final = (
    plot
    * plot_avg
    * plot_above
    * plot_below
    * scatter_2023
    * text_days_above
    * text_days_below
    * text_above
    * text_below
).opts(
    xlabel="TIME OF YEAR",
    ylabel="MAX TEMP °F",
    title="SEATTLE 2023 vs AVERAGE (1990-2020)",
    gridstyle={"ygrid_line_alpha": 0},
    xticks=[
        (1, "JAN"),
        (31, "FEB"),
        (59, "MAR"),
        (90, "APR"),
        (120, "MAY"),
        (151, "JUN"),
        (181, "JUL"),
        (212, "AUG"),
        (243, "SEP"),
        (273, "OCT"),
        (304, "NOV"),
        (334, "DEC"),
    ],
    show_grid=True,
    fontscale=1.18,
)
hvplot.show(final)

```
user: ahuang11
issue_13673: state: open
title: [FEATURE] Add Carbon Theme
body: ### Problem description

I'd like to create prettier dark plots like
https://medium.com/@pYdeas/create-publication-styled-figures-with-hvplot-using-the-bokeh-backend-b623ab48a5d2

### Feature description

Just an additional theme

### Potential alternatives

Use other themes

### Additional information

_No response_
user: ahuang11
labels: tag: CSS: CSS, styling, theming and widget design.
issue_13666: state: open
title: [FEATURE] expand edge_renderer glyph to handle more than just multiline
body: ### Problem description

This pertains to network graphs --> https://docs.bokeh.org/en/latest/docs/user_guide/topics/graph.html#network-graphs . The current setup is to instantiate a `StaticLayoutProvider` with a dict that containing node-node connections (e.g. 'start' and 'end'). From what I can tell, this layout provider does some sort of default transform that looks up the corresponding xy coordinates of the start/end nodes, and translates that into xs and ys coords for a multi line.

e.g.

```
nds = {'x':[5,10,11],'y':[15,12,9]
layout = {'start':[0,0],'end':[1,2]}
# some transform happening that turns the above into this:
edge_renderer_data = {'xs':[ [5,10], [5,11] ] , 'ys': [ [15,12], [15,9] ] }
```

Then if you want to make these edges NOT simple connectors, you have to manually define explicit paths by essentially hard-coding the xs and ys values on the edge_renderer. The problem with doing this is a) it's inefficient if you want to define your path with a function, and b) it's "dangerous" (and alluded to in the docs) because users are responsible for making their paths actually connect between the nodes.

### Feature description

There are actually a lot of glyphs in bokeh that in theory you should be able to use for an edge renderer. Bezier and Segment are obvious to me me but there are probably others. The feature I'm requesting is to provide the ability (and an example implementation) to assign alternate glyphs that use alternate transformations to translate 'start'/'end' node info into information that the selected glyph can use to cleanly and efficiently draw the edges.

### Potential alternatives

I drafted a basic example/idea using CustomJSTransform that doesn't use GraphRenderer or StaticLayoutProvider at all  -->

```
import math
from bokeh.plotting import figure, show
from bokeh.models import  Ellipse, Segment, ColumnDataSource, CustomJSTransform
from bokeh.transform import transform
from bokeh.palettes import Spectral8

# list the nodes and initialize a plot
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)

# create lists of x- and y-coordinates
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]

node_source = ColumnDataSource(data={'x':x,'y':y,'fill_color':Spectral8})
edge_source = ColumnDataSource(data={'start':[0 for x in node_indices[1:]]
                                     ,'end':[x for x in node_indices[1:]]})

node_glyph = Ellipse(height=0.1, width=0.2, fill_color="fill_color")
node_rend = plot.add_glyph(node_source,node_glyph)

edge_tr_x = CustomJSTransform(args=dict(ns=node_source)
                            ,v_func='''
                            return xs.map(ni=>ns.data['x'][ni])
                            ''')
edge_tr_y = CustomJSTransform(args=dict(ns=node_source)
                            ,v_func='''
                            return xs.map(ni=>ns.data['y'][ni])
                            ''')
edge_glyph = Segment(x0=transform(field_name='start',transform=edge_tr_x)
                     ,y0=transform(field_name='start',transform=edge_tr_y)
                     ,x1=transform(field_name='end',transform=edge_tr_x)
                     ,y1=transform(field_name='end',transform=edge_tr_y)                    
                     )
edge_rend = plot.add_glyph(edge_source,edge_glyph)

show(plot)

```

But obviously if I want to leverage all the interaction policies and other nice stuff already built into GraphRenderer, I will have to write a lot more CustomJS/CustomJSTransform/CustomJSHover etc..... Furthermore I'm looping through edge_source four times in this example to get x0,x1,y0, and y1 coordinates.... which in theory i should only have to do once (e.g. take the node source and the edge source and transform it into a derivative datasource used to drive Segment.


### Additional information

_No response_
user: gmerritt123
issue_13652: state: open
title: [FEATURE] `HTMLLabel` to support `stylesheets` and `css_classes` 
body: ### Problem description

The `HTMLLabel` or `Label` annotations are very useful renderers for text since they accept screen units. One issue for me is the lack of css style options beyond the current text, border and background arguments. I often find that I would like to style something but not able to. Eg it could be padding, use the font-family defined in html body css, font-weight. I have previously asked about this on [discourse](https://discourse.bokeh.org/t/is-it-possible-to-style-a-htmllabel/10044).

Based on the following versions:
Python version        :  3.11.7 (main, Jan  1 2024, 01:39:59) [Clang 14.0.0 (clang-1400.0.29.202)]
IPython version       :  (not installed)
Tornado version       :  6.4
Bokeh version         :  3.3.3

### Feature description

Other widgets support the arguments `css_classes` and `stylesheets` and, if I have understood correctly, `HTMLLabel` is a `div` element. 

### Potential alternatives

Have not considered any alternatives.

### Additional information

_No response_
user: carve11
issue_13650: state: open
title: Add support for formatters to `ValueRef`
body: Example (side-by-side comparison):

![Screenshot from 2024-01-17 15-12-58](https://github.com/bokeh/bokeh/assets/27475/f83c7a10-95f6-4286-bcf8-eb5b098ecef4)

```py
import numpy as np

from bokeh.plotting import figure, show

N = 4000
x = np.random.random(size=N) * 100
y = np.random.random(size=N) * 100
radii = np.random.random(size=N) * 1.5
colors = np.array([(r, g, 150) for r, g in zip(50+2*x, 30+2*y)], dtype="uint8")

TOOLS="crosshair,pan,wheel_zoom,zoom_in,zoom_out,box_zoom,undo,redo,reset,tap,save,box_select,poly_select,lasso_select,examine,help"

p = figure(tools=TOOLS)

p.circle(x, y, radius=radii, fill_color=colors, fill_alpha=0.6, line_color=None)

from bokeh.models import HoverTool, Styles
from bokeh.models.dom import Div, ValueRef, Index, Template

style = Styles(
    display="grid",
    grid_template_columns="auto auto",
    column_gap="10px",
)
grid = Div(style=style)
grid.children = [
    "index",  Div(children=["#", Index()]),
    "(x, y)", Div(children=["(", ValueRef(field="x"), ", ", ValueRef(field="y")]),
    "radius", ValueRef(field="radius", format="%.2f", formatter="printf"),
]

hover0 = HoverTool(
    tooltips=Template(children=[grid]),
    attachment="left",
)
p.add_tools(hover0)

hover1 = HoverTool(
    tooltips=[
        ("index", "$index"),
        ("(x,y)", "(@x, @y)"),
        ("radius", "@radius{%.2f}"),
    ],
    formatters={
        "@radius": "printf",
    },
    attachment="right",

)
p.add_tools(hover1)
```

fixes #13599

user: mattpap
issue_13646: state: open
title: [FEATURE] Support BoxSelectTool-like range-setting for the RangeTool
body: ### Problem description

Limiting the setting of the focused range to just pan/drag with a RangeTool adds extra steps/clicks.

### Feature description

Is there any interest in supporting BoxSelectTool-like range-setting interaction for the RangeTool by default? For example, see [plotly range slider](https://plotly.com/python/range-slider/) or the Bokeh app below that uses a custom callback.

### Potential alternatives

None

### Additional information


```python

import numpy as np
from bokeh.layouts import column
from bokeh.models import ColumnDataSource, RangeTool, BoxSelectTool, CustomJS
from bokeh.plotting import figure, show
from bokeh.sampledata.stocks import AAPL
from bokeh.io import output_notebook

output_notebook()

dates = np.array(AAPL['date'], dtype=np.datetime64)
source = ColumnDataSource(data=dict(date=dates, close=AAPL['adj_close']))

# Main
p = figure(height=300, width=800, tools="xpan", toolbar_location=None,
           x_axis_type="datetime", x_axis_location="above",
           background_fill_color="#efefef", x_range=(dates[1500], dates[2500]))
p.line('date', 'close', source=source)
p.yaxis.axis_label = 'Price'

# Minimap
select = figure(title="Drag the middle and edges of the selection box to change the range above",
                height=130, width=800, y_range=p.y_range,
                x_axis_type="datetime", y_axis_type=None,
                tools="", background_fill_color="#efefef")
select.line('date', 'close', source=source)
select.ygrid.grid_line_color = None

range_tool = RangeTool(x_range=p.x_range)
select.add_tools(range_tool)

bselect = BoxSelectTool(dimensions='width')
select.add_tools(bselect)

callback = CustomJS(args=dict(x_range=p.x_range), code="""
        const geometry = cb_obj.geometry;
        x_range.start = geometry.x0;
        x_range.end = geometry.x1;
""")

select.js_on_event('selectiongeometry', callback)

show(column(p, select))

```

https://github.com/bokeh/bokeh/assets/6613202/3174e247-48cc-44e1-9fb6-941d2438458f



user: droumis
labels: grant: CZI R5: Funded by CZI Round 5 grant
issue_13645: state: open
title: [FEATURE] Emit warning when data too large to render as image
body: ### Problem description

Currently, if trying to plot an image from an array that is beyond a certain size, Bokeh will just display an empty plot. This can be confusing.

### Feature description

Consider emitting a warning when dataset is too large to plot.

### Potential alternatives

None

### Additional information

```python
from bokeh.plotting import figure, show
from bokeh.io import output_notebook
from bokeh.layouts import row
import numpy as np
output_notebook()

data1 = np.random.rand(2, 60000)
data2 = np.random.rand(2, 70000)
data3 = np.random.rand(1, 70000)

p1 = figure(width=300, height=150)
p2 = figure(width=300, height=150)
p3 = figure(width=300, height=150)

p1.image(image=[data1], x=0, y=0, dw=2, dh=60000)
p2.image(image=[data2], x=0, y=0, dw=2, dh=70000)
p3.image(image=[data3], x=0, y=0, dw=1, dh=70000)

show(row(p1, p2, p3))

```

<img width="916" alt="image" src="https://github.com/bokeh/bokeh/assets/6613202/88d1dcdc-7b4e-48e5-9a7f-39168477c383">

user: droumis
issue_13644: state: open
title: Users Guide sections for server CLI is not useful
body: It merely links to the refguide, but even the refguide links are still a non-obvious click or two away from actual information. 

<img width="1157" alt="Screenshot 2024-01-08 at 10 29 26" src="https://github.com/bokeh/bokeh/assets/1078448/b9dae3af-fa70-432a-b4ed-2597c3ba633d">

It would be good to replicate the information here, one way or another, directly in the user's guide:

https://docs.bokeh.org/en/latest/docs/reference/command/subcommands/serve.html#bokeh-command-subcommands-serve

user: bryevdv
issue_13643: state: open
title: Documentation job in CI silently fails with `cannot stat ...`
body: Full error message:
```
cp: cannot stat '../../src/bokeh/server/static': No such file or directory
```
See e.g. in https://github.com/bokeh/bokeh/actions/runs/7416176428/job/20180761135?pr=13635.

We also have to make sure that any such issues in the documentation build are being surfaced, because we've been having an ongoing situation where we discover documentation issues after PRs introducing them were merged.
user: mattpap
issue_13642: state: open
title: [BUG] add_next_tick_callback doesn't update widget after upgrading bokeh 3.1->3.2
body: ### Software versions

Python version      :  3.11.7 | packaged by Anaconda, Inc. | (main, Dec 15 2023, 18:05:47) [MSC v.1916 64 bit (AMD64)]
IPython version     :  (not installed)                                                                                
Tornado version     :  6.3.3                                                                                          
Bokeh version       :  3.2.0                                                                                          
BokehJS static path :  C:\Users\xxx\AppData\Local\anaconda3\envs\rplot_3.11\Lib\site-packages\bokeh\server\static
node.js version     :  v20.10.0
npm version         :  10.2.3
Operating system    :  Windows-10-10.0.19045-SP0


### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

Update widget state after execution of python-callback completed

### Observed behavior

nothing with widgets happened. Alright in bokeh versions <= 3.1

### Example code

```Python
import time
from bokeh.server.tornado import BokehTornado
from bokeh.server.server import BaseServer
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop
from bokeh.server.util import bind_sockets
from bokeh.application import Application
from bokeh.layouts import column
from bokeh.application.handlers import FunctionHandler
from bokeh.models import CustomJS, Button, Div, Select


NAVIGATION_COLUMN_WIDTH = 315
spinner_text = """
<!-- https://www.w3schools.com/howto/howto_css_loader.asp -->
            <div class="loader">
            <style scoped>
            .loader {
            border: 5px solid #f3f3f3;
              border-radius: 50%;
              border-top: 5px solid #3498db;
              border-bottom: 5px solid #3498db;
              width: 30px;
              height: 30px;
            animation: spin 2s linear infinite;
            }

            @keyframes spin {
                0% { transform: rotate(0deg); }
                100% { transform: rotate(360deg); }
            }
            </style>
            </div>
"""
def modify_doc(doc):
    reaction_select = Select(title="Choice:", options=['1', '2'], value='1',
                                        width=NAVIGATION_COLUMN_WIDTH - 55)
    button_switch = Button(label="Switch", button_type="success", width=NAVIGATION_COLUMN_WIDTH)
    div_spinner = Div(text="", width=20, height=20, align='end')

    def hide_spinner():
        div_spinner.text = ""
        button_switch.disabled = False
        return

    def switch(event):
        time.sleep(3)
        doc.add_next_tick_callback(hide_spinner)

    button_switch.on_click(switch)
    button_switch.js_on_click(CustomJS(
        args=dict(button_switch=button_switch, div_spinner=div_spinner, text=spinner_text),
        code="""
                    button_switch.disabled = true
                    div_spinner.text = text
                    console.log('switch reaction')
                    """))

    doc.add_root(column(reaction_select, button_switch, div_spinner))

rplot_server = Application(FunctionHandler(modify_doc))

bokeh_tornado = BokehTornado({'/rplot': rplot_server},
                                 extra_websocket_origins=["localhost:8001"],
                                 generate_session_ids=True,
                                 session_token_expiration=100000000,
                                 keep_alive_milliseconds=60000,
                                 check_unused_sessions_milliseconds=60000,
                                 unused_session_lifetime_milliseconds=1000000,
                                 websocket_max_message_size_bytes=1500000000

                                 )

bokeh_http = HTTPServer(bokeh_tornado) #, ssl_options=ssl_ctx
sockets, port = bind_sockets('127.0.0.1', 8001)
bokeh_http.add_sockets(sockets)
server = BaseServer(IOLoop.current(), bokeh_tornado, bokeh_http)
server.start()
server.io_loop.start()
```


### Stack traceback or browser console output

_No response_

### Screenshots

![image](https://github.com/bokeh/bokeh/assets/8910350/0a956e82-c7ce-4689-8d35-e5935af5b46f)

user: dem865
issue_13634: state: open
title: Drop support for Python 3.9 and modernize the codebase
body: This PR drops support for Python 3.9 and takes advantage of the following PEPs: 634 (pattern matching), 604 (`A | B`), 613 (`TypeAlias`), 647 (`TypeGuard`). I converted a few obvious cases from `if-elif-else` to `match-case` as a trial run. I tenatatively added this PR to 3.4 milestone, but I suppose we may want to postpone this till 3.5.
user: mattpap
labels: tag: codebase: Improvements to code quality without affecting any functionality.
issue_13631: state: open
title: DOC Missing vertical gap between grid items in First Steps guides
body: I couldn't find an option for submitting documentation-related issues, so I had to resort to this option (sorry if I wasn't meant to use this specific issue type).

## Expected behaviour

There should be a vertical gap between the grid elements on this [doc page](https://docs.bokeh.org/en/latest/docs/first_steps.html#first-steps-guides).

## Observed behaviour

There is no vertical gap between the grid components.

![Screenshot 2024-01-04 153708](https://github.com/bokeh/bokeh/assets/87762282/17753824-1b5d-4afb-9548-5d52a383850e)

## How to fix

Using the Chrome Dev Tools, I've located the relevant CSS that controls this vertical gap.

However I am unsure how to update the CSS in the codebase, which is why I created this issue instead of directly submitting a PR.

![Screenshot 2024-01-04 153851](https://github.com/bokeh/bokeh/assets/87762282/feee31ad-b014-4c96-ae79-a7146ebf1059)
![Screenshot 2024-01-04 153942](https://github.com/bokeh/bokeh/assets/87762282/dbb74c27-7d5a-4b57-a4d3-f57ebd874af5)

user: raj-pulapakura
issue_13623: state: open
title: [BUG] TextAreaInput resizable=False doesn't set resize: none
body: ### Software versions

Python version        :  3.10.13 | packaged by conda-forge | (main, Oct 26 2023, 18:09:17) [Clang 16.0.6 ]
IPython version       :  8.18.1
Tornado version       :  6.3.3
Bokeh version         :  3.3.2
BokehJS static path   :  [/Users/ahuang/miniconda3/envs/panel/lib/python3.10/site-packages/bokeh/server/static](https://file+.vscode-resource.vscode-cdn.net/Users/ahuang/miniconda3/envs/panel/lib/python3.10/site-packages/bokeh/server/static)
node.js version       :  v18.16.0
npm version           :  9.5.1
jupyter_bokeh version :  3.0.7
Operating system      :  macOS-13.6.2-arm64-arm-64bit

### Browser name and version

Chrome

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

I shouldn't be able to resize (manually set `resize: none` in browser)

<img width="210" alt="image" src="https://github.com/bokeh/bokeh/assets/15331990/6f64401d-78b4-47e6-b4c4-4939f764161f">

<img width="318" alt="image" src="https://github.com/bokeh/bokeh/assets/15331990/8f9ce418-2ecf-4293-b468-7f2ba3fc90a5">



### Observed behavior

I can resize

<img width="396" alt="image" src="https://github.com/bokeh/bokeh/assets/15331990/1ce76b71-7ce6-4738-9fa5-86a3e48ddca8">


### Example code

```Python
from bokeh.models import TextAreaInput
from bokeh.io import output_notebook, show
from IPython.display import display

# Output to the Jupyter Notebook
output_notebook()

# Create a non-resizable TextAreaInput
text_input = TextAreaInput(value="Type here", title="Non-Resizable TextAreaInput", resizable=False)

# Display the widget in the notebook
display(text_input)

# Optionally, if you want to show the Bokeh plot with show()
show(text_input)
```


### Stack traceback or browser console output

_No response_

### Screenshots

_No response_
user: ahuang11
issue_13622: state: open
title: x_axis_type='datetime' DatetimeTickFormatter Translate the name of the mounths from English to Italian
body: ### Problem description

I'm using x_axis_type='datetime' DatetimeTickFormatter and I wish translate name of the mounths from English to Italian. 
You can add this to the Bokeh 
I'm attaching an example 

```
from bokeh.plotting import figure, output_file, show
from bokeh.io import output_notebook, show
from bokeh.plotting import figure
import datetime
from bokeh.models import DatetimeTickFormatter
x = [datetime.datetime.strptime('20231220', '%Y%m%d')
    ,datetime.datetime.strptime('20231230', '%Y%m%d')]
y = ['1','2']
# create a new plot with a title and axis labels
p = figure(title="example chart", x_axis_label='x', y_axis_label='y',
            x_axis_type='datetime',x_range=(x[0], x[1]))
# add a line renderer with legend and line thickness
p.line(x, y, line_width=2)
defaultDatetimeTickFormatter=DatetimeTickFormatter(microseconds = '%fus'
                                        ,milliseconds = '%3Nms'
                                        ,seconds = '%Ss'
                                        ,minsec = ':%M:%S'
                                        ,minutes = ':%M'
                                        ,hourmin = '%H:%M'
                                        ,hours = '%Hh'
                                        ,days = '%d/%b'
                                        ,months = '%b/%Y'
                                        ,years = '%Y')
p.xaxis.formatter = defaultDatetimeTickFormatter
# show the results
show(p)
```


### Feature description

 I wish translate name of the mounths from English to Italian. 

### Potential alternatives

I don't know of any alternative

### Additional information

_No response_
user: mauropu
issue_13620: state: open
title: Add support for outline shapes to `Text`-like glyphs
body: Early work in progress. This PR adds support other outline shapes than box/rectangle to `Text`-like glyphs. Shapes are circumscribed onto the bounding box of text boxes, so that all content fit inside those shapes.

Example (various shapes with `Text` and `TeX` glyphs):
![image](https://github.com/bokeh/bokeh/assets/27475/54218b95-8a23-4887-95c9-51fbeb465e17)

fixes #8289
user: mattpap
labels: grant: SDG 2023: Funded from NumFOCUS Small Development Grant 2023
issue_13619: state: open
title: Add support for glyph decorations/markers
body: Early work in progress. Glyph decorations were added previously, but only in a very limited and experimental fashion. This PR's goal is to improve upon that and establish decorations as a feature and add it to all relevant glyphs. The specific applications of this feature are directed graphs and vector fields.

Example showing `Arc`, `Ray`, `MultiLine` and `Bezier` glyphs:
![image](https://github.com/bokeh/bokeh/assets/27475/2518f3d4-f17e-44c1-a558-baa848e0ad4f)

fixes #8129
user: mattpap
labels: grant: SDG 2023: Funded from NumFOCUS Small Development Grant 2023
issue_13617: state: open
title: [BUG] Hover CustomJS callback only triggered on last added glyph where glyphs share CDS
body: ### Software versions

Python version        :  3.9.18 (main, Nov  2 2023, 17:01:24) 
IPython version       :  (not installed)
Tornado version       :  6.4
Bokeh version         :  3.3.2

### Browser name and version

Firefox 120.0.1 (64-bit)

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

I have a `CDS` with a field, `x`,  for data along x-axis and three different fields, `y1, y2, y3` for plotting series (y-axis). I use the `scatter` glyph to plot the three series where each glyph´s `name` propterty is equal to the field that it is plotting. I attach a `HoverTool` to each renderer. The `HoverTool` got a `CustomJS` callback attached where I print, `console.log`, the `name` property of the renderer and the index of a hovered glyphs `source` (`source.inspected.indices`). 

I would expect the `CustomJS` callback to be triggered for each datapoint when hovered. Hence, the console of the browser should print the name and indicate the index of the datapoint that is hovered, eg. if I hover y2 series and the 2nd data point, then I would expect: `y2, Array[1]`. 
The tooltip of the hover shows correct information of each hovered datapoint. 

### Observed behavior

I observe that the `CustomJS` callback only gets triggered when hover a data point of the **last** added renderer `y3`. And here I get the callback information for **all** renderer, eg
```
y1 
Array [ 3 ]
y2 
Array [ 3 ]
y3 
Array [ 3 ]
```
I have also tried to use a common `HoverTool` where I do not specify the renderers. I observe the same that it is only when hovering the **last** added renderer `y3` that the callback is triggered.

### Example code

```Python
from bokeh.plotting import figure, show
from bokeh.models import ColumnDataSource, CustomJS, HoverTool

src = ColumnDataSource(
    data = {
        'x': [1,2,3,4],
        'y1': [1,2,3,4],
        'y2': [2,3,4,5],
        'y3': [3,4,5,6]
    }
)
style = {
    'y1': {'marker': 'circle', 'color': 'orange'},
    'y2': {'marker': 'square', 'color': 'blue'},
    'y3': {'marker': 'triangle', 'color': 'red'},
}

p = figure(width=400, height=400, tools = '')

for i in style:
    r =  p.scatter(
        x = 'x', y = i, 
        marker = style[i]['marker'],
        color = style[i]['color'], 
        size = 12, alpha = 0.5,
        source = src,
        name = i,
        legend_label = i
        )

    p.add_tools(HoverTool(
        tooltips = [
            (i, f'@{i}'),
            ('index', '$index'),
        ],
        renderers = [r],
        callback = CustomJS(
            args = {'src': src, 'rend': r},
            code='''
                const idx = src.inspected.indices;
                if (idx.length > 0) {
                    console.log(rend.name);
                    console.log(idx);
                }
            ''')
    ))

p.legend[0].location = 'bottom_right'

# this does not work either, callback only triggered on last 
# added glyph
# p.add_tools(HoverTool(
#     callback = CustomJS(
#       args = {'src': src},
#       code='''
#             const idx = src.inspected.indices;
#             if (idx.length > 0) {
#               console.log(idx);
#             }
#         ''')
# ))

show(p)
```


### Stack traceback or browser console output

_No response_

### Screenshots

_No response_
user: carve11
issue_13616: state: open
title: [BUG] DateRangePicker value does not reflect value in browser when selection is aborted
body: ### Software versions

Python version        :  3.10.13 (main, Dec 19 2023, 03:47:28) [GCC 12.2.0]
IPython version       :  (not installed)
Tornado version       :  6.4
Bokeh version         :  3.3.2
BokehJS static path   :  /usr/local/lib/python3.10/site-packages/bokeh/server/static
node.js version       :  (not installed)
npm version           :  (not installed)
jupyter_bokeh version :  (not installed)
Operating system      :  Linux-6.6.6-200.fc39.x86_64-x86_64-with-glibc2.36

### Expected behavior

If only one date is selected when the calendar is closed (_e.g._ if you click elsewhere), I would expect the value on the Python side to reflect what is displayed in the browser.

### Observed behavior

In practice the field in the browser is cleared, so you could expect the value to be reset to `None`. Instead, the previous value persists.

### Example code

```Python
from bokeh.models import DateRangePicker, Button, PreText
from bokeh.io import curdoc
from bokeh.layouts import column, row


def callback(_):
    col.children.append(PreText(text=str(date_range_picker.value)))


date_range_picker = DateRangePicker(
    title="Select date range",
    value=("2019-09-20", "2019-10-15"),
    min_date="2019-08-01",
    max_date="2019-10-30",
    width=400,
)

button = Button(label="Display date range")
button.on_click(callback)
col = column(button)

curdoc().add_root(row(date_range_picker, col))
```

### Screenshots

![date_range](https://github.com/bokeh/bokeh/assets/49269671/161bd1c8-016d-48d6-b95e-d2e879429a91)
user: TheoMathurin
issue_13614: state: open
title: Gallery links need to be versioned
body: https://github.com/bokeh/bokeh/pull/13539 partially addressed an issue with gallery links pointing to out of date `branch-3.0` by pointing to `main` instead, but this is only a bandaid fix, since we do not reliably update `main` either (but at least it will never be entirely missing).

The proper fix to this would be to make sure the docs generate properly versioned links, e.g. to `branch-3.4` for 3.4.x releases or even to the specific tag if possible. This might require yet another new small sphinx extension. 

user: bryevdv
issue_13600: state: open
title: [BUG] Zoom tool requires all the renderers in a subplot context
body: ### Software versions

Python version        :  3.10.13 | packaged by conda-forge | (main, Oct 26 2023, 18:09:17) [Clang 16.0.6 ]
IPython version       :  8.18.1
Tornado version       :  6.3.3
Bokeh version         :  3.3.2
BokehJS static path   :  /Users/mliquet/miniconda3/envs/hv310/lib/python3.10/site-packages/bokeh/server/static
node.js version       :  (not installed)
npm version           :  (not installed)
jupyter_bokeh version :  (not installed)
Operating system      :  macOS-13.5.2-arm64-arm-64bit

### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

@mattpap hinted at the fact that I shouldn't have to pass an explicit list of all the renders when creating a zoom tool in a subplot context, as the default value of `'auto'` should lead to including all of them.

### Observed behavior

Without the explicit link of renderers, it looks like the zoom tool is behaving as if `level=0`.

### Example code

```Python
import numpy as np

from bokeh.core.properties import field
from bokeh.io import show
from bokeh.models import ColumnDataSource, Div, FactorRange, ZoomInTool, ZoomOutTool, Range1d
from bokeh.plotting import figure

n_channels = 5
n_seconds = 15

total_samples = 512*n_seconds
time = np.linspace(0, n_seconds, total_samples)
data = np.random.randn(n_channels, total_samples).cumsum(axis=1)
channels = [f"EEG {i}" for i in range(n_channels)]

x_range = Range1d(start=time.min(), end=time.max())
y_range = FactorRange(factors=channels)

p = figure(x_range=x_range, y_range=y_range, lod_threshold=None, tools="pan,reset")

source = ColumnDataSource(data=dict(time=time))
renderers = []

for i, channel in enumerate(channels):
    xy = p.subplot(
        x_source=p.x_range,
        y_source=Range1d(start=data[i].min(), end=data[i].max()),
        x_target=p.x_range,
        y_target=Range1d(start=i, end=i + 1),
    )

    source.data[channel] = data[i]
    line = xy.line(field("time"), field(channel), source=source, name=channel)
    renderers.append(line)

zoom_kwargs = dict(
    # NOTE: uncomment for this to work as expected
    # renderers=renderers,
    level=1,
    dimensions="height"
)

zoom_in = ZoomInTool(**zoom_kwargs)
zoom_out = ZoomOutTool(**zoom_kwargs)
p.add_tools(zoom_in, zoom_out)

show(p)
```


### Stack traceback or browser console output

_No response_

### Screenshots

_No response_
user: maximlt
issue_13599: state: open
title: [FEATURE] Support formatters when using Template as HoverTool
body: ### Problem description

When using `Template` as a tooltip in a `HoverTool`. I would like it to be possible to also use formatters. 

### Feature description

I want these two HoverTools to provide the same output (except styling):

``` python
import pandas as pd
import numpy as np

from bokeh.models import ColumnDataSource, HoverTool
from bokeh.plotting import figure, show
from bokeh.models.dom import Div, Styles, Template, ValueRef


source = ColumnDataSource(
    data=dict(x=[0], y=[0], date=np.array([np.datetime64("2020-01-01")]))
)


def template_tooltips():
    style = Styles(
        display="grid",
        grid_template_columns="auto auto",
        column_gap="10px",
    )
    grid = Div(style=style)
    grid.children = [
        "date",
        ValueRef(field="date{%F}"),
    ]
    return Template(children=[grid])


hovertool1 = HoverTool(
    tooltips=[("date", "@date{%F}")],
    formatters={"@date": "datetime"},
)


hovertool2 = HoverTool(
    tooltips=template_tooltips(),
    formatters={"@date": "datetime"},
)

p = figure(width=400, height=400)
p.add_tools(hovertool1)
p.add_tools(hovertool2)
p.circle("x", "y", size=20, source=source)

show(p)
```

Normal hover tool:
![image](https://github.com/bokeh/bokeh/assets/19758978/51cd702e-a0ee-4334-9357-20e4c0128f9a)

Template hover tool
![image](https://github.com/bokeh/bokeh/assets/19758978/b23b13a3-cc6f-4e84-a30e-7a12b73e5070)

Note: Ignore the styling problem this is because I'm using Firefox. See #13520.


### Potential alternatives

As an alternative, I can use custom HTML to get the same effect, but it will quickly be somewhat ugly.  

### Additional information

For my real purpose, I plan to use this for ImageStack. A rough example is given here: https://github.com/holoviz/holoviews/issues/6015#issuecomment-1853731270. 


user: Hoxbro
issue_13595: state: open
title: Redesign pinch zoom and improve its behavior
body: This is an early work in progress that aims to properly define the behavior of pinch zoom (a part of `WheelZoomTool`). This is partially a follow up after PR #12831, though the problem with this kind of zoom was known for a long time. The underlying pinch gesture works correctly, as can be seen when pinch resizing `BoxAnnotation`. I'm actually reusing the logic from that annotation to maintain state during handling of pinch gesture in the zoom tool.
user: mattpap
issue_13579: state: open
title: Toolbar_sticky has no effect
body: On the current docs setting `toolbar_sticky` has no effect, the toolbar dodges the axis in both cases. Should this property be deprecated?

ref: https://docs.bokeh.org/en/latest/docs/user_guide/interaction/tools.html#positioning-the-toolbar

<img width="673" alt="Screenshot 2023-12-04 at 19 41 37" src="https://github.com/bokeh/bokeh/assets/1078448/15ba37c9-03ed-40b3-9524-0706ae8c18c2">

cc @bokeh/dev 
user: bryevdv
issue_13578: state: open
title: Unit tests are failing due to changes in pytest-asyncio
body: They fail with:
```
pytest.PytestWarning: The test <Function test_allow_websocket_origin> is marked with
'@pytest.mark.asyncio' but it is not an async function. Please remove the asyncio mark.
If the test is not marked explicitly, check for global marks applied via 'pytestmark'.
```
I verified a few of the offending test functions and all are defined as `async def`.

I will pin `pytest-asyncio` for now.
user: mattpap
issue_13571: state: open
title: Allow to customize toolbars with any UI components
body: This is an early preview PR. This PR continues work started in PR #12417, by exposing the ability of configuring tool buttons instead of just tools to bokeh. It however expands on that work, allowing toolbar to include any UI elements (e.g. select boxes).

![image](https://github.com/bokeh/bokeh/assets/27475/86764900-cce9-4991-bb61-454bda47939a)

```py
import numpy as np

from bokeh.plotting import figure, show
from bokeh.models import Toolbar, PaletteSelect
import bokeh.models.tools as t

N = 4000
x = np.random.random(size=N) * 100
y = np.random.random(size=N) * 100
radii = np.random.random(size=N) * 1.5
colors = np.array([(r, g, 150) for r, g in zip(50+2*x, 30+2*y)], dtype="uint8")

select = PaletteSelect(
    value="Red",
    items=[("Red", ["red"]), ("Green", ["green"]), ("Blue", ["blue"])],
    swatch_width=20,
)

children = [
    t.OnOffButton(tool=t.PanTool()),
    t.OnOffButton(tool=t.BoxSelectTool(persistent=True)),
    None,
    select,
    None,
    t.ClickButton(tool=t.ZoomInTool()),
    t.ClickButton(tool=t.ZoomOutTool()),
    None, # or Divider()
    t.ClickButton(tool=t.ResetTool()),
]

tb = Toolbar(children=children)
p = figure(toolbar=tb, toolbar_location="above", width=400, height=400)

p.circle(x, y, radius=radii, fill_color=colors, fill_alpha=0.6, line_color=None)
show(p)
```

Continues work on #8153, which was partially addressed by PR #13593.
user: mattpap
issue_13570: state: open
title: Add documentation and tests for non-visual overrides (PR #13554)
user: mattpap
issue_13566: state: open
title: [BUG] Legend outside plot area won’t resize
body: ### Software versions

Python version        :  3.12.0 | packaged by conda-forge | (main, Oct  3 2023, 08:26:13) [MSC v.1935 64 bit (AMD64)]
IPython version       :  (not installed)
Tornado version       :  6.3.3
Bokeh version         :  3.3.1
BokehJS static path   :  C:\Users\Blaise\miniconda3\envs\testenv312\Lib\site-packages\bokeh\server\static
node.js version       :  (not installed)
npm version           :  (not installed)
jupyter_bokeh version :  (not installed)
Operating system      :  Windows-10-10.0.19045-SP0

### Browser name and version

Chrome Version 119.0.6045.161 (Offizieller Build) (64-Bit)

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

In a plot with a legend right, outside of the plot area, while changing the text of the legend to a longer text with `p.legend.items[0].label = {'value': "Longer legend"}`, I expect the width of the plot area and of the legend to adapt automatically to fit the width of the figure.

### Observed behavior

After updating the legend to a longer text, the text is updated but the legend overlaps the plot area.

### Example code

```Python
from bokeh.io import curdoc
from bokeh.plotting import figure
from bokeh.models import Legend, LegendItem, Button
from bokeh.layouts import column

# Prepare some data
x = [1, 2, 3, 4, 5]
y = [6, 7, 2, 4, 5]

# Create a new plot
p = figure(x_axis_label="x", y_axis_label="y", toolbar_location="above", height=200)

# Create an empty legend right, outside of the plotting area:
p.add_layout(Legend(items=[]), "right")
p.legend.click_policy="hide" # Clicking on the legend after callback "repairs" the layout.

# Add renderer:
line = p.line(x, y)
p.legend[0].items.append(LegendItem(label="Temp.", renderers=[line]))

# Define callback with triggering button:
button = Button(label="Click")

def callback():
    p.legend.items[0].label = {'value': "Longer legend"}

button.on_click(callback)

# Define layout:
my_layout = column(button, p)

curdoc().add_root(my_layout)
```


### Stack traceback or browser console output

The expected width ratios can be achieved by clicking twice on the legend to hide and show back the curve.
Though, I didn't find a workaround to enforce the expected size programmatically.

### Screenshots

Initial appearance:
![ini](https://github.com/bokeh/bokeh/assets/94801369/cc22fea4-4662-400c-9090-f1e3de46f22c)
Appearance after callback:
![After callback](https://github.com/bokeh/bokeh/assets/94801369/4736ba23-6fa9-4aa0-814d-abd240d52db4)

user: Icoti
issue_13562: state: open
title: Optimize code paths involving data array indices
body: This is a very early work in progress. The goal of this PR is to optimize handling of data indices.
user: mattpap
labels: grant: CZI R5: Funded by CZI Round 5 grant
issue_13551: state: open
title: Redesign and improve slider widgets
body: This PR replaces noUiSlider-based implementation of slider widgets with an "in-house" implementation. This is an early work in progress that preserves (so far) most of the original functionality, improves and simplifies styling, reuses bokehjs' components like tooltips and implements more types sliders.

This PR also adds rudimentary support for property overrides, like:
```py
class BaseSlider(Model):
  value = Any()
class NumberSlider(BaseSlider):
  value = Override(Float())
```

Example showing normal and stealth sliders:

![image](https://github.com/bokeh/bokeh/assets/27475/ae2abad9-479c-46c7-a784-e1d8a54eabbe)

Example showing customization via stylesheets (see `examples/styling/widgets/color_sliders.py`):

[Screencast from 27.11.2023 00:44:19.webm](https://github.com/bokeh/bokeh/assets/27475/17ee5501-f64d-44f2-89a9-ea46a81c6834)

- [x] fixes #10081
- [ ] fixes #13513
- [ ] fixes #7772
- [ ] fixes #8012
- [ ] fixes #9015
- [ ] fixes #8797
- [ ] fixes #10444
- [ ] fixes #7117
- [x] fixes #9012
- [ ] fixes #11860
user: mattpap
issue_13547: state: open
title: Add support for deferred loading of bokehjs' bundles
body: This is an experiment in adding support for deferred bundles. Bundles are resolved the same as previously, but instead of including them via resource management, they are included either by name or respective URL in document's serialized representation and resolved by bokehjs when needed. This work for both standalone and server embedding. Additional bundles can be requested with subsequent document patches.

In the example below we can see that only `bokeh.js` was loaded with the initial HTML page, but we can use widgets and math text:

![image](https://github.com/bokeh/bokeh/assets/27475/0c64d6a6-728a-4923-9c29-2b55445925d2)

Tentatively including in 3.4 milestone, but it may take some time before this is fully incorporated into bokeh's embed.

- [ ] fixes #10166 
- [x] fixes #10782

user: mattpap
issue_13546: state: open
title: Add support for progress widgets
body: [Screencast from 23.11.2023 13:43:49.webm](https://github.com/bokeh/bokeh/assets/27475/94b8cef8-c563-468d-8cef-e54033218d1c)

fixes #6556
user: mattpap
labels: tag: CSS: CSS, styling, theming and widget design.
issue_13544: state: open
title: Docs build on Windows not working
body: Building the docs on Windows doesn't seem to work. I have cloned a fresh copy of the repo on Win10 (about 14 days ago) and set everything up as described in our [contributor guide](https://docs.bokeh.org/en/latest/docs/dev_guide/documentation.html#setting-up-and-building-bokeh-s-documentation). When I run `make.bat html`, the following things happen (tested on two separate Win10 machines and with both branch-3.4 and branch-3.3 - with the same outcome):

```
(bkdev) C:\github\bokeh\docs\bokeh>make.bat html
Running Sphinx v7.2.6
C:\github\bokeh\src\bokeh\sphinxext\bokeh_gallery.py:44: RemovedInSphinx80Warning: The alias 'sphinx.util.status_iterator' is deprecated, use 'sphinx.util.display.status_iterator' instead. Check CHANGES for Sphinx API modifications.
  from sphinx.util import ensuredir, status_iterator
C:\github\bokeh\src\bokeh\sphinxext\bokeh_plot.py:104: RemovedInSphinx80Warning: The alias 'sphinx.util.status_iterator' is deprecated, use 'sphinx.util.display.status_iterator' instead. Check CHANGES for Sphinx API modifications.
  from sphinx.util import copyfile, ensuredir, status_iterator
C:\github\bokeh\src\bokeh\sphinxext\bokeh_sitemap.py:41: RemovedInSphinx80Warning: The alias 'sphinx.util.status_iterator' is deprecated, use 'sphinx.util.display.status_iterator' instead. Check CHANGES for Sphinx API modifications.
  from sphinx.util import status_iterator
making output directory... done
creating gallery file entries... [100%] missing_dates.rst
[autosummary] generating autosummary for: docs/dev_guide.rst, docs/dev_guide/bindings.rst, docs/dev_guide/bokehjs.rst, docs/dev_guide/creating_issues.rst, docs/dev_guide/debug_headless_chrome.rst, docs/dev_guide/documentation.rst, docs/dev_guide/env_vars.rst, docs/dev_guide/models.rst, docs/dev_guide/pull_requests.rst, docs/dev_guide/python.rst, ..., docs/user_guide/topics/contour.rst, docs/user_guide/topics/geo.rst, docs/user_guide/topics/graph.rst, docs/user_guide/topics/hex.rst, docs/user_guide/topics/hierarchical.rst, docs/user_guide/topics/images.rst, docs/user_guide/topics/pie.rst, docs/user_guide/topics/stats.rst, docs/user_guide/topics/timeseries.rst, index.rst
BokehDeprecationWarning: 'tile_providers module' was deprecated in Bokeh 3.0.0 and will be removed, use 'add_tile directly' instead.
loading intersphinx inventory from https://numpy.org/doc/stable/objects.inv...
loading intersphinx inventory from https://pandas.pydata.org/pandas-docs/stable/objects.inv...
loading intersphinx inventory from https://docs.python.org/3/objects.inv...
loading intersphinx inventory from https://www.sphinx-doc.org/en/master/objects.inv...
loading intersphinx inventory from https://xyzservices.readthedocs.io/en/stable/objects.inv...
building [mo]: targets for 0 po files that are out of date
writing output...
building [html]: targets for 610 source files that are out of date
updating environment: [new config] 610 added, 0 changed, 0 removed
 * Serving Flask app 'bokeh_app_9b25ccc505b0478d9f5ba38c8c3ac53a'
 * Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:5050
Press CTRL+C to quit
```
-> The docs begin to build but at some point the server started (I only called `make.bat html`, NOT  `make.bat serve`). When I open the URL in my browser, I only see a 404 error.

Next, I hit Ctrl+C to quit the server. The following output happens:

```
127.0.0.1 - - [08/Nov/2023 16:29:02] "GET / HTTP/1.1" 404 -
 * Serving Flask app 'bokeh_app_fd60335a86254100a2a6631df1f58797'ents_source
 * Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:5050
Press CTRL+C to quit
```
i.e. the server just restarted (still 404). I hit CTRL+C again, and the following happens:

```
copying bokeh-plot files... [100%] bokeh-content-fbe67463385d4aabb3ee0fe6f09fd2c2-docs-examples-basic-bars-bar.js
adding links to sitemap...

Sphinx error:
Error generating bokeh-content-2f8ebb4ba27240b6be1c650f99705f86-docs-examples-basic-layouts-custom_layout.js:

bokeh-plot:: error:

Traceback (most recent call last):
  File "C:\github\bokeh\src\bokeh\application\handlers\code_runner.py", line 229, in run
    exec(self._code, module.__dict__)
  File "C:\github\bokeh\examples\basic\layouts\custom_layout.py", line 61, in <module>
    save([p0, p1, p2], template=template)
  File "C:\github\bokeh\src\bokeh\sphinxext\example_handler.py", line 89, in _add_root
    curdoc().add_root(obj)
  File "C:\github\bokeh\src\bokeh\document\document.py", line 321, in add_root
    with self.models.freeze():
  File "C:\Users\Timo Metzger\anaconda3\envs\bkdev\lib\contextlib.py", line 142, in __exit__
    next(self.gen)
  File "C:\github\bokeh\src\bokeh\document\models.py", line 135, in freeze
    self._pop_freeze()
  File "C:\github\bokeh\src\bokeh\document\models.py", line 288, in _pop_freeze
    self.recompute()
  File "C:\github\bokeh\src\bokeh\document\models.py", line 215, in recompute
    new_models |= mr.references()
AttributeError: 'list' object has no attribute 'references'


evaluating source:

from bokeh.io import save
from bokeh.plotting import figure
from bokeh.util.browser import view

template = """
{% block preamble %}
<style>
* { box-sizing: border-box; }
.plots { display: flex; flex-direction: row; width: 100%; }
.p { width: 33.3%; padding: 50px; }
.p:nth-child(1) { background-color: red; }
.p:nth-child(2) { background-color: green; }
.p:nth-child(3) { background-color: blue; }
</style>
{% endblock %}
{% block body %}
<body style="background-color: lightgray;">
    {{ self.inner_body() }}
</body>
{% endblock %}
{% block contents %}
<div>
<p>This example shows how different Bokeh Document roots may be embedded in custom
templates. The individual plots were embedded in divs using the embed macro:

<pre>
    &lt;div class="p"&gt;&#123;&#123; embed(roots.p0) &#125;&#125;&lt;/div&gt;
    &lt;div class="p"&gt;&#123;&#123; embed(roots.p1) &#125;&#125;&lt;/div&gt;
    &lt;div class="p"&gt;&#123;&#123; embed(roots.p2) &#125;&#125;&lt;/div&gt;
</pre>

And the divs are styled using standard CSS in the template:

<pre>
    .p { width: 33.3%; padding: 50px; }
    .p:nth-child(1) { background-color: red; }
    .p:nth-child(2) { background-color: green; }
    .p:nth-child(3) { background-color: blue; }
</pre>
</p>
</div>
<div class="plots">
    <div class="p">{{ embed(roots.p0) }}</div>
    <div class="p">{{ embed(roots.p1) }}</div>
    <div class="p">{{ embed(roots.p2) }}</div>
</div>
</div>
{% endblock %}
"""

x = [1, 2, 3]
y = [1, 2, 3]

p0 = figure(name="p0", sizing_mode="scale_width")
p0.scatter(x, y, size=20, fill_color="red")
p1 = figure(name="p1", sizing_mode="scale_width")
p1.scatter(x, y, size=20, fill_color="green")
p2 = figure(name="p2", sizing_mode="scale_width")
p2.scatter(x, y, size=20, fill_color="blue")

save([p0, p1, p2], template=template)
view("custom_layout.html")

File not found - static
0 File(s) copied
Terminate batch job (Y/N)? n
```

It seems like several strange things are happening here... 

I usually build the docs in WSL - I would expect `make.bat html` to work just like  `make html`  works in WSL!

cc @bryevdv @vicadesoba 

user: tcmetzger
issue_13526: state: open
title: [BUG] Plot sizing not working with css transitions (scale)
body: ### Software versions

Python version      :  3.8.5 (tags/v3.8.5:580fbb0, Jul 20 2020, 15:57:54) [MSC v.1924 64 bit (AMD64)]
IPython version     :  (not installed)
Tornado version     :  6.3.3
Bokeh version       :  3.1.1
BokehJS static path :  c:\users\xa235208\documents\developments\bug_bokeh\.venv\lib\site-packages\bokeh\server\static
node.js version     :  v16.14.2
npm version         :  8.5.0
Operating system    :  Windows-10-10.0.19041-SP0

### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

Bokeh figure correctly scaling after css transition

### Observed behavior

When a css transition is used to scale an html element, bokeh figure with sizing_mode="stretch_width" does not rescale correctly


### Example code

```Python
from bokeh.embed import components
from bokeh.plotting import figure
from bokeh.resources import Resources
from jinja2 import Template

# dummy plot
x_f = [1.5, 2, 9]
y_f = [3, 3, 3.1]
p1 = figure(height=350, sizing_mode="stretch_width")
p1.line(x_f, y_f)
script1, div1 = components(p1)
p2 = figure(height=350, sizing_mode="stretch_width")
p2.line(x_f, y_f)
script2, div2 = components(p2)


style = """
<style>
* {
    box-sizing: border-box;
}

.tabs {
    padding-left: 10px;
    padding-right: 10px;
    display: flex;
    width: 100%;
    border-radius: 8px 8px 0 0;
}

.tabby-tab label {
    display: block;
    box-sizing: border-box;
    /* tab content must clear this */
    height: 40px;

    padding: 10px;
    text-align: center;
    background: #9768D1;
    cursor: pointer;
    transition: background 0.5s ease;

}

.tabby-tab label:hover {
    background: #7B52AB;
}

.tabby-content {
    position: absolute;
    top: 40px;

    transition:
        opacity 0.8s ease,
        transform 0.8s ease;
        right 0.8s ease;
        left 0.8s ease;

    /* show/hide */
    opacity: 0;
    transform: scale(0.1);
    transform-origin: top left;

}

.tabby-tab [type=radio] {
    display: none;
}

[type=radio]:checked~label {
    background: #553285;
    z-index: 2;
}

[type=radio]:checked~label~.tabby-content {
    z-index: 1;
    opacity: 1;
    transform: scale(1);
    left: 10px;
    right: 10px;
}

.plots {
    width: 100%;
}

.plot {
    height: 350px;
}
</style>
"""

template = Template(
    """<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        {{ resources }}
        {{ style }}
        {{ script1 }}
        {{ script2 }}
    </head>
    <body>
        <section>
            <div class="tabs">
                <div class="tabby-tab">
                    <input type="radio" id="tab-1" name="tabby-tabs" checked>
                    <label for="tab-1">Tab1</label>
                    <div class="tabby-content">
                        <div class="plot">
                            {{div2}}
                        </div>
                    </div>
                </div>
                <div class="tabby-tab">
                    <input type="radio" id="tab-2" name="tabby-tabs">
                    <label for="tab-2">Tab2</label>
                    <div class="tabby-content">
                        <div class="plot">
                            {{div1}}
                        </div>
                    </div>
                </div>
            </div>
        </section>
    </body>
</html>
"""
)

resources = Resources().render()

html = template.render(
    resources=resources,
    style=style,
    script1=script1,
    script2=script2,
    div1=div1,
    div2=div2,
)
with open("test.html", "w") as file:
    file.write(html)
```


### Stack traceback or browser console output

_No response_

### Screenshots

![bokeh_sizing_bug](https://github.com/bokeh/bokeh/assets/18531147/6284e45c-a9d3-4ffd-a4f1-f8f9bf50d7e4)

user: xavArtley
issue_13522: state: open
title: Migrate to SlickGrid 5.x
body: Very early work in progress.

fixes #13423

user: mattpap
labels: tag: CSS: CSS, styling, theming and widget design.
issue_13521: state: open
title: [BUG] static resources changed from relative to absolute urls in 3.3.0
body: ### Software versions

Python version      :  3.10.11 | packaged by conda-forge | (main, May 10 2023, 19:07:22) [Clang 14.0.6 ]
IPython version     :  8.13.2
Tornado version     :  6.3.3
Bokeh version       :  3.3.0
BokehJS static path :  /Users/jtomlinson/miniconda3/envs/dask/lib/python3.10/site-packages/bokeh/server/static
node.js version     :  (not installed)
npm version         :  (not installed)
Operating system    :  macOS-13.5.2-x86_64-i386-64bit

### Expected behavior

In `bokeh==3.2.2` static urls were relative.

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    ...
    <script type="text/javascript" src="static/js/bokeh.min.js?v=3ca6425586de5036dc01992dd69aa61e9196dd02619557cfaeb1b3d8b77adf724be49401b1168483d165494ce57a6daa16e6f6d3660fef117d45028221f86357"></script>
...
```

### Observed behavior

However after upgrading to `3.3.0` they became absolute.

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    ...
    <script type="text/javascript" src="/static/js/bokeh.min.js?v=39ef57c3a83533e24f961e5c27f651a61045dbccefac4b5df86a7680b1edaff31886a7c0322250ffb0d758fa14ae156c9b640f60cca99f020096b050a4dbb571"></script>
...
```

This causes problems when we try to view the page via some proxy outside of our control that puts our server on a different path. Absolute URLs cause the JS and CSS files to be looked for in the wrong place.

### Example code

In Dask we already have a tornado server so we use the `BokehTornado` server to add it. This is a distilled version of what we do in Dask that reproduces the problem.

```python
from bokeh.server.server import BokehTornado
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop

bokeh_app = BokehTornado({"/": lambda _: None})
http_server = HTTPServer(bokeh_app)
http_server.listen(port=1234)
IOLoop.current().start()
```

Then visit http://localhost:1234 and view the page source.
user: jacobtomlinson
issue_13519: state: open
title: Make CI docs build artifacts easily accessible
body: Whenever CI/CD runs, the docs get build. At the same time, it can be time-consuming and complex to build docs locally, especially for new contributors who don't have a local dev system set up (and maybe only want to contribute a small docs update). Therefore, it would be helpful to have a convenient way to access the docs that are build during a CI/CD run. This would also allow the Bokeh team to easily review docs updates in a PR without having to check out the PR and build the docs locally. 

This probably entails two aspects:
- updating workflow to make the built docs accessible
- update the contributor docs (https://docs.bokeh.org/en/latest/docs/dev_guide/documentation.html) with information on how to access the built docs

(cc @bryevdv)
user: tcmetzger
issue_13515: state: open
title: CI unit-test on Python 3.9 fails systematically 
body: Noticed on multiple recent PRs, e.g. https://github.com/bokeh/bokeh/actions/runs/6759027374/job/18371768064?pr=13512:

```py
=================================== FAILURES ===================================
___________________ test__autocreate_signed_session_autoload ___________________

cls = <class '_pytest.runner.CallInfo'>
func = <function call_runtest_hook.<locals>.<lambda> at 0x7fc4ed3c35e0>
when = 'call'
reraise = (<class '_pytest.outcomes.Exit'>, <class 'KeyboardInterrupt'>)

    @classmethod
    def from_call(
        cls,
        func: "Callable[[], TResult]",
        when: "Literal['collect', 'setup', 'call', 'teardown']",
        reraise: Optional[
            Union[Type[BaseException], Tuple[Type[BaseException], ...]]
        ] = None,
    ) -> "CallInfo[TResult]":
        """Call func, wrapping the result in a CallInfo.
    
        :param func:
            The function to call. Called without arguments.
        :param when:
            The phase in which the function is called.
        :param reraise:
            Exception or exceptions that shall propagate if raised by the
            function, instead of being wrapped in the CallInfo.
        """
        excinfo = None
        start = timing.time()
        precise_start = timing.perf_counter()
        try:
>           result: Optional[TResult] = func()

/usr/share/miniconda3/envs/bk-test/lib/python3.9/site-packages/_pytest/runner.py:341: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/usr/share/miniconda3/envs/bk-test/lib/python3.9/site-packages/_pytest/runner.py:262: in <lambda>
    lambda: ihook(item=item, **kwds), when=when, reraise=reraise
/usr/share/miniconda3/envs/bk-test/lib/python3.9/site-packages/pluggy/_hooks.py:493: in __call__
    return self._hookexec(self.name, self._hookimpls, kwargs, firstresult)
/usr/share/miniconda3/envs/bk-test/lib/python3.9/site-packages/pluggy/_manager.py:115: in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
/usr/share/miniconda3/envs/bk-test/lib/python3.9/site-packages/_pytest/unraisableexception.py:88: in pytest_runtest_call
    yield from unraisable_exception_runtest_hook()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    def unraisable_exception_runtest_hook() -> Generator[None, None, None]:
        with catch_unraisable_exception() as cm:
            yield
            if cm.unraisable:
                if cm.unraisable.err_msg is not None:
                    err_msg = cm.unraisable.err_msg
                else:
                    err_msg = "Exception ignored in"
                msg = f"{err_msg}: {cm.unraisable.object!r}\n\n"
                msg += "".join(
                    traceback.format_exception(
                        cm.unraisable.exc_type,
                        cm.unraisable.exc_value,
                        cm.unraisable.exc_traceback,
                    )
                )
>               warnings.warn(pytest.PytestUnraisableExceptionWarning(msg))
E               pytest.PytestUnraisableExceptionWarning: Exception ignored in: <coroutine object HTTPServer.close_all_connections at 0x7fc4ec[444](https://github.com/bokeh/bokeh/actions/runs/6759027374/job/18371768064?pr=13512#step:8:445)c40>
E               
E               Traceback (most recent call last):
E                 File "/usr/share/miniconda3/envs/bk-test/lib/python3.9/asyncio/selector_events.py", line 293, in _add_writer
E                   key = self._selector.get_key(fd)
E                 File "/usr/share/miniconda3/envs/bk-test/lib/python3.9/selectors.py", line 193, in get_key
E                   raise KeyError("{!r} is not registered".format(fileobj)) from None
E               KeyError: '77 is not registered'
E               
E               During handling of the above exception, another exception occurred:
E               
E               Traceback (most recent call last):
E                 File "<string>", line 1, in <lambda>
E               KeyError: '__import__'

/usr/share/miniconda3/envs/bk-test/lib/python3.9/site-packages/_pytest/unraisableexception.py:78: PytestUnraisableExceptionWarning
```
user: mattpap
issue_13513: state: open
title: Add support for ticks and tick labels to sliders
body: This is follow-up after PR #13489. It would be useful to have ticks and tick labels in general in sliders, but specifically for `CategoricalSlider`. The underlying uislider supports this (pips), but the CSS has display issues that need to be resolved.
user: mattpap
issue_13509: state: open
title: [BUG] Incorrectly formatted ticks with %N format when time is older than the Epoch
body: Note: this is a repost of #13067, which was mistakenly closed.
---

### Software versions

Python version      :  3.9.13 (main, Oct 13 2022, 21:23:06) [MSC v.1916 64 bit (AMD64)]
IPython version     :  (not installed)
Tornado version     :  6.2
Bokeh version       :  3.1.0
BokehJS static path :  C:\ProgramData\Miniconda3\lib\site-packages\bokeh\server\static
node.js version     :  (not installed)
npm version         :  (not installed)
Operating system    :  Windows-10-10.0.19045-SP0

### Browser name and version

Visble in both Firefox and Chrome

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

This is a correct result:
![correct](https://user-images.githubusercontent.com/12662295/230246063-7c78d4ce-8146-46fe-aec6-e3a4088c7096.png)
If the code below is modified so the point is in 1969 instead of 1970, I get the incorrect result:
![incorrect](https://user-images.githubusercontent.com/12662295/230246143-a4e560ec-6499-4d12-959f-d86b3e6dda49.png)
I would have expected the microsecond part of the tickers to only contain digits, not a negative sign.


### Observed behavior

See above

### Example code

```Python
import datetime
from bokeh.plotting import figure, show
from math import pi
from bokeh.models import DatetimeTickFormatter


plot = figure()
plot.scatter((datetime.datetime(1970,12,31,1,2,3),), (4,))
plot.xaxis.formatter = DatetimeTickFormatter(
			microseconds = '%Y-%m-%d %H:%M:%S.%3N'
		)
plot.xaxis.major_label_orientation = pi/2
show(plot)
```


### Stack traceback or browser console output

_No response_

### Screenshots

_No response_
user: jmranger
issue_13508: state: open
title: [BUG] legend label with click policy has gray background on muted items
body: ### Expected behavior

I expect that the "hide" policy for legend labels has no effect on the background of the legend items.

### Observed behavior

If I click in a themed figure on a legend label to hide the renderers, the background of this item changes.

### Example code

```Python
from bokeh.plotting import curdoc, figure, show, output_notebook
output_notebook()

x = [1, 2, 3, 4, 5]
y = [6, 7, 6, 4, 5]

curdoc().theme = 'dark_minimal'

p = figure(title='dark_minimal', width=300, height=300)
p.line(x, y, legend_label='line')
p.legend.click_policy="hide"

show(p)
```

### Screenshots

![grafik](https://github.com/bokeh/bokeh/assets/68053396/23e9f175-1903-4b9d-94ae-871ad286d971)

<details><summary>technical information</summary>
### Software versions

Python version      :  3.10.12 | packaged by conda-forge | (main, Jun 23 2023, 22:40:32) [GCC 12.3.0]
IPython version     :  8.14.0
Tornado version     :  6.3.2
Bokeh version       :  3.3.0
BokehJS static path :  /opt/conda/envs/aiva-env/lib/python3.10/site-packages/bokeh/server/static
node.js version     :  v18.16.0
npm version         :  9.5.1
Operating system    :  Linux-4.19.0-21-amd64-x86_64-with-glibc2.35

### Browser name and version

Chrome Version 119.0.6045.106 
</details>

user: mosc9575
issue_13504: state: open
title: [BUG] Zooming inverts axis
body: ### Software versions

Python version      :  3.10.13 | packaged by conda-forge | (main, Oct 26 2023, 18:09:17) [Clang 16.0.6 ]
IPython version     :  8.17.2
Tornado version     :  6.3.3
Bokeh version       :  3.3.0
BokehJS static path : /Users/droumis/opt/miniconda3/envs/timeseries/lib/python3.10/site-packages/bokeh/server/static
node.js version     :  v20.4.0
npm version         :  9.7.2
Operating system    :  macOS-13.5.2-arm64-arm-64bit

### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

Observed in both Jupyter Lab 4 and VS Code

### Expected behavior

I don't expect zoom to invert an axis.

### Observed behavior

zooming occasionally inverts the y-axis.

### Example code

```Python
from bokeh.plotting import figure, show
from bokeh.io import output_notebook

output_notebook()

x = [1, 2, 3, 4, 5]
y = [6, 7, 2, 4, 5]

p = figure(title="simple line example", x_axis_label='x', y_axis_label='y')

p.line(x, y, legend_label="Temp.", line_width=2)

show(p)
```


### Stack traceback or browser console output

_No response_

### Screenshots


https://github.com/bokeh/bokeh/assets/6613202/3cacb80b-877e-42da-bf3c-2db008e0d659


user: droumis
issue_13498: state: open
title: [BUG] v3 vs v2 of bokeh
body: ### Software versions

Python version      :  3.10.8 | packaged by conda-forge | (main, Nov 22 2022, 08:25:29) [Clang 14.0.6 ]
IPython version     :  8.7.0
Tornado version     :  6.2
Bokeh version       :  3.0.0
BokehJS static path :  /Users/hyunmink/mambaforge/envs/lab/lib/python3.10/site-packages/bokeh/server/static
node.js version     :  v20.9.0
npm version         :  10.1.0
Operating system    :  macOS-13.6-arm64-arm-64bit

### Browser name and version

Firefox, Chrome

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

Visible with v2.4.3

In versions 3.0.0 and later, the plot is not visible.


### Observed behavior

Not seen

### Example code

```Python
https://github.com/aws-samples/amazon-omics-tutorials/blob/main/utils/scripts/timeline.py
```


### Stack traceback or browser console output

_No response_

### Screenshots


v3.0.0 and later
<img width="692" alt="v3 0 0" src="https://github.com/bokeh/bokeh/assets/4966343/23dfd083-be8a-461b-ade2-70908e41b316">

v2.4.3
<img width="1214" alt="v2 4 3" src="https://github.com/bokeh/bokeh/assets/4966343/5769d60c-9885-4ff8-9633-9084a3fc5ba5">

user: hmkim
issue_13494: state: open
title: [FEATURE] Data Line Width
body: ### Problem description

This is just a follow up on [stack](https://stackoverflow.com/questions/60411960/using-data-line-widths-in-bokeh)

The owner of the stack thread decided not to issue a feature request, but I decided to do so.

### Feature description

Simply allowing specification of line width in data coordinates.

I had similar issue (although not exactly the same) with circles, where circles were not stretched according to data coordinates, but fortunately there was already an alternative - `ellipse`, which did exactly what I needed.

However, I can not seem to find anything simple for lines, except for...

### Potential alternatives

drawing polygons manually. However, this is a fairly involved process for operation, which is very basic for many vector libraries. In this instance, I was using `kiva`, which wraps many other libraries and line width seems to be in data coordinates by default in all of those.

So I think having this, would make a more seamless transition for those navigating from those libraries towards bokeh.

### Additional information

Thank you.
user: dgrigonis
issue_13477: state: open
title: Dev docs no longer report precise version information
body: On previous versions of the docs the version switcher showed the exact version of the dev/rc build:

<img width="762" alt="Screenshot 2023-10-20 at 16 16 17" src="https://github.com/bokeh/bokeh/assets/1078448/7b62f08f-7a14-460e-b367-9ed058d68026">

---

On latest `dev-3.4` it just says "Choose version"

<img width="586" alt="Screenshot 2023-10-20 at 16 15 08" src="https://github.com/bokeh/bokeh/assets/1078448/1a4c2964-2ed3-4e4a-8809-18a2197b649b">

cc @tcmetzger in case anything immediate comes to mind. I am fairly certain this is due to a sphinx theme update, maybe there is something we can update or else possibly worth filing an issue on their tracker.

user: bryevdv
issue_13476: state: open
title: [FEATURE] Categorical y_range for RangeTool
body: ### Software versions

Python version      :  3.10.12 | packaged by conda-forge | (main, Jun 23 2023, 22:41:52) [Clang 15.0.7 ]
IPython version     :  8.16.1
Tornado version     :  6.3.3
Bokeh version       :  3.3.0
BokehJS static path :  /Users/droumis/opt/miniconda3/envs/bokeh3.3/lib/python3.10/site-packages/bokeh/server/static
node.js version     :  v20.4.0
npm version         :  9.7.2
Operating system    :  macOS-13.5.2-arm64-arm-64bit

### Browser name and version

Google Chrome Version 118.0.5993.70 (Official Build) (arm64)

### Jupyter notebook / Jupyter Lab version

Jupyter Lab Version 4.0.7

### Expected behavior

I expected the categorical y_range for RangeTool to work. Maybe there's a fundamental reason for it not being able to?

### Observed behavior

Categorical y_range for RangeTool fails to validate

### Example code

```Python
CATEGORICAL = True

import numpy as np

from bokeh.core.properties import field
from bokeh.io import show, output_notebook
from bokeh.layouts import column, row
from bokeh.models import (ColumnDataSource, CustomJS, Div, FactorRange, HoverTool,
                          Range1d, Switch, WheelZoomTool, ZoomInTool, ZoomOutTool)
from bokeh.palettes import Category10
from bokeh.plotting import figure

output_notebook()

n_channels = 10
n_seconds = 15
fs = 512 # Hz
total_samples = fs*n_seconds
time = np.linspace(0, n_seconds, total_samples)
data = np.random.randn(n_channels, total_samples).cumsum(axis=1)
channels = [f"EEG {i}" for i in range(n_channels)]

hover = HoverTool(tooltips=[
    ("Channel", "$name"),
    ("Time", "$x s"),
    ("Amplitude", "$y µV"),
])

x_range = Range1d(start=time.min(), end=time.max())

if CATEGORICAL:
    y_range = FactorRange(factors=channels)
else:
    y_range = Range1d(start=-0.5, end=len(channels) - 1 + 0.5)

p = figure(x_range=x_range, y_range=y_range, lod_threshold=None, tools="pan,reset")

source = ColumnDataSource(data=dict(time=time))
renderers = []

for i, channel in enumerate(channels):
    if CATEGORICAL:
        y_target=Range1d(start=i, end=i + 1)
    else:
        y_target=Range1d(start=i - 0.5, end=i + 0.5)
    
    xy = p.subplot(
        x_source=p.x_range,
        y_source=Range1d(start=data[i].min(), end=data[i].max()),
        x_target=p.x_range,
        y_target=y_target,
    )

    source.data[channel] = data[i]
    line = xy.line(field("time"), field(channel), color=Category10[10][i], source=source, name=channel)
    renderers.append(line)

if not CATEGORICAL:
    from bokeh.models import FixedTicker
    ticks = list(range(len(channels)))
    p.yaxis.ticker = FixedTicker(ticks=ticks)
    p.yaxis.major_label_overrides = {i: f"EEG {i}" for i in ticks}

level = 1

ywheel_zoom = WheelZoomTool(renderers=renderers, level=level, dimensions="height")
xwheel_zoom = WheelZoomTool(renderers=renderers, level=level, dimensions="width")
zoom_in = ZoomInTool(renderers=renderers, level=level, dimensions="height")
zoom_out = ZoomOutTool(renderers=renderers, level=level, dimensions="height")

p.add_tools(ywheel_zoom, xwheel_zoom, zoom_in, zoom_out, hover)
p.toolbar.active_scroll = ywheel_zoom

z_data = zscore(data, axis=1)

range_tool = RangeTool(x_range=p.x_range, y_range=p.y_range)
range_tool.x_range.update(start=4, end=6)
range_tool.y_range.update(start=2, end=8)

select = figure(height=130, #y_range=p.y_range,
                tools="", toolbar_location=None)
select.image(image=[z_data], x=0, y=0, dw=n_seconds, dh=n_channels, palette="Sunset11")
select.add_tools(range_tool)

show(column(p, select))
```


### Stack traceback or browser console output

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[22], line 76
     72 p.toolbar.active_scroll = ywheel_zoom
     74 z_data = zscore(data, axis=1)
---> 76 range_tool = RangeTool(x_range=p.x_range, y_range=p.y_range)
     77 range_tool.x_range.update(start=4, end=6)
     78 range_tool.y_range.update(start=2, end=8)

File ~/opt/miniconda3/envs/bokeh3.3/lib/python3.10/site-packages/bokeh/models/tools.py:471, in RangeTool.__init__(self, *args, **kwargs)
    470 def __init__(self, *args, **kwargs) -> None:
--> 471     super().__init__(*args, **kwargs)

File ~/opt/miniconda3/envs/bokeh3.3/lib/python3.10/site-packages/bokeh/models/tools.py:180, in Tool.__init__(self, *args, **kwargs)
    179 def __init__(self, *args, **kwargs) -> None:
--> 180     super().__init__(*args, **kwargs)

File ~/opt/miniconda3/envs/bokeh3.3/lib/python3.10/site-packages/bokeh/model/model.py:110, in Model.__init__(self, *args, **kwargs)
    107 if "id" in kwargs:
    108     raise ValueError("initializing 'id' is not allowed")
--> 110 super().__init__(**kwargs)
    111 default_theme.apply_to_model(self)

File ~/opt/miniconda3/envs/bokeh3.3/lib/python3.10/site-packages/bokeh/core/has_props.py:302, in HasProps.__init__(self, **properties)
    300     if value is Undefined or value is Intrinsic:
    301         continue
--> 302     setattr(self, name, value)
    304 initialized = set(properties.keys())
    305 for name in self.properties(_with_props=True): # avoid set[] for deterministic behavior

File ~/opt/miniconda3/envs/bokeh3.3/lib/python3.10/site-packages/bokeh/core/has_props.py:334, in HasProps.__setattr__(self, name, value)
    332 properties = self.properties(_with_props=True)
    333 if name in properties:
--> 334     return super().__setattr__(name, value)
    336 descriptor = getattr(self.__class__, name, None)
    337 if isinstance(descriptor, property): # Python property

File ~/opt/miniconda3/envs/bokeh3.3/lib/python3.10/site-packages/bokeh/core/property/descriptors.py:331, in PropertyDescriptor.__set__(self, obj, value, setter)
    328     class_name = obj.__class__.__name__
    329     raise RuntimeError(f"{class_name}.{self.name} is a readonly property")
--> 331 value = self.property.prepare_value(obj, self.name, value)
    332 old = self._get(obj)
    333 self._set(obj, old, value, setter=setter)

File ~/opt/miniconda3/envs/bokeh3.3/lib/python3.10/site-packages/bokeh/core/property/bases.py:365, in Property.prepare_value(self, owner, name, value, hint)
    363 else:
    364     obj_repr = owner if isinstance(owner, HasProps) else owner.__name__
--> 365     raise ValueError(f"failed to validate {obj_repr}.{name}: {error}")
    367 if isinstance(owner, HasProps):
    368     obj = owner

ValueError: failed to validate RangeTool(id='p4883', ...).y_range: expected either None or a value of type Instance(Range1d), got FactorRange(id='p4692', ...)

### Screenshots

When NOT categorical y_range:

<img width="622" alt="image" src="https://github.com/bokeh/bokeh/assets/6613202/350bfc6b-9fb3-422e-8d11-e873db2cdbd1">

user: droumis
issue_13475: state: open
title: Add option to set optical marker on an axis
body: Sometimes it is difficult in a multi axis case to figure out which data belongs to which axis. One solutions is shown in the [twin_axes example](https://docs.bokeh.org/en/latest/docs/examples/basic/axes/twin_axes.html) where the labels are used to explain which color belongs to which axis. This is handy as long as only one renderer belongs to one axis.

I have implemented a different approach as a basic prototype. I set a "marker" on the axis as an anchor.

![optical_marker_on_axis](https://github.com/bokeh/bokeh/assets/68053396/f3c1bda0-18c1-4cd1-9d10-01ca21983b85)

I think it would be cool to add this feature to bokeh and also, if possible, place the marker on the axis and not close to the visible area.

user: mosc9575
issue_13469: state: open
title: Add unit property to axis
body: It would be nice to have a unit per axis to avoid redunant and user unfriendly ticking.

The figure below is an example where the unit is set per tick.

![grafik](https://github.com/bokeh/bokeh/assets/68053396/483829f8-427d-42f7-95e3-b92679c521f3)

What I have in mind is something like below:
![Unit-Tick](https://github.com/bokeh/bokeh/assets/68053396/90c77398-7c07-43e9-8781-e17ab47e2eef)

And this is how I would expect the potential code:

```
plot = figure(height=200)
plot.title = "Current over Resistance at a static voltage of 1 volt"
plot.circle(x, y, fill_color="blue", size=5)
plot.line(x, y, color="darkgrey")

plot.xaxis.axis_label = "Resistance"
plot.xaxis.unit = "$$\Omega$$"
plot.yaxis.axis_label = "Current"
plot.yaxis.unit = "$$A$$"
show(plot)
```

## Why is this useful?

I can think of some reasons:

1. Less use of `major_label_overrides` which simplifies the usage
2. Shorter ticks but keeping the same information
3. potential option to add timezone information to a datetime axis

user: mosc9575
issue_13467: state: open
title: Add scale up boundary to datetimetickformatter
body: All pull requests must have an associated issue in the issue tracker. If there
isn't one, please go open an issue describing the defect, deficiency or desired
feature. You can read more about our issue and PR processes in the
[wiki](https://github.com/bokeh/bokeh/wiki/BEP-1:-Issues-and-PRs-management).

- [ ] issues: adresses #13186 
- [ ] tests added / passed
- [ ] release document entry (if new feature or API change)

user: muendlein
issue_13460: state: open
title: [BUG] DataTable autosize_mode="fit_viewport" raises ReferenceError
body: ### Expected behavior

My expectation is, that all `autosize_mode`s of the `DataTable` can be displayed without an error.

### Observed behavior

The two modes `"fit_viewport"` and `"fit_columns"` both raise a `ReferenceError` shown in the Browser Console.

```
VM188 bokeh-tables-3.3.0.min.js:92
    Uncaught (in promise) ReferenceError: row is not defined
    at getColContentSize (VM188 bokeh-tables-3.3.0.min.js:92:48514)
    at getColAutosizeWidth (VM188 bokeh-tables-3.3.0.min.js:92:47153)
    at SlickGrid.autosizeColumns (VM188 bokeh-tables-3.3.0.min.js:92:44466)
    at M.updateLayout (VM188 bokeh-tables-3.3.0.min.js:55:3085)
    at M._after_render (VM188 bokeh-tables-3.3.0.min.js:55:6623)
    at M.after_render (VM185 bokeh-3.3.0.min.js:560:6522)
    at M.r_after_render (VM185 bokeh-3.3.0.min.js:560:6487)
    at M.render_to (VM185 bokeh-3.3.0.min.js:560:6323)
    at VM185 bokeh-3.3.0.min.js:218:834
    at async u (bokeh-3.3.0.min.js:218:657)
```


### Example code

```Python
from datetime import date
from random import randint

from bokeh.models import ColumnDataSource, DataTable, DateFormatter, TableColumn
from bokeh.io import show, output_notebook
output_notebook()

data = dict(
    dates=[date(2023, 3, i+1) for i in range(3)],
    downloads=[randint(0, 100) for i in range(3)],
)
source = ColumnDataSource(data)

columns = [
    TableColumn(field="dates", title="Date", formatter=DateFormatter()),
    TableColumn(field="downloads", title="Downloads"),
]

data_table_viewport = DataTable(
    source=source,
    columns=columns,
    width=400,
    height=100,
    autosize_mode = "fit_viewport", # same for "fit_columns"
)
show(data_table_viewport)
```
<details>
<summary>Software versions</summary>
Python version      :  3.10.12 | packaged by conda-forge | (main, Jun 23 2023, 22:40:32) [GCC 12.3.0]
IPython version     :  8.14.0
Tornado version     :  6.3.2
Bokeh version       :  3.3.0
BokehJS static path :  /opt/conda/envs/aiva-env/lib/python3.10/site-packages/bokeh/server/static
node.js version     :  v18.16.0
npm version         :  9.5.1
Operating system    :  Linux-4.19.0-21-amd64-x86_64-with-glibc2.35

Chrome Version 118.0.5993.71
</details>
user: mosc9575
issue_13440: state: open
title: [BUG] logrithmic axis allows scaling by the pan tool
body: ### Software versions

Python version      :  3.11.6 | packaged by conda-forge | (main, Oct  3 2023, 10:40:35) [GCC 12.3.0]
IPython version     :  8.16.1
Tornado version     :  6.3.3
Bokeh version       :  3.2.2
BokehJS static path :  /home/stgardner4/micromamba/envs/acars/lib/python3.11/site-packages/bokeh/server/static
node.js version     :  (not installed)
npm version         :  (not installed)
Operating system    :  Linux-6.5.5-200.fc38.x86_64-x86_64-with-glibc2.37

### Browser name and version

Brave Version 1.58.137 Chromium: 117.0.5938.153

### Jupyter notebook / Jupyter Lab version

jupyterlab 4.0.7

### Expected behavior

With bounds set on a figure's x/y range, attempting to click drag to pan against a bound should do nothing

### Observed behavior

When using a logarithmic y-axis, it is possible to zoom the plot by dragging against the pan limit set by the bounds, and there is no way to disable this behavior.

### Example code

```Python
from bokeh.models import Range1d, WheelZoomTool
from bokeh.plotting import figure, show
import numpy as np

x = np.linspace(1, 10, 100)
y = 10**(x/10)

fig = figure()
fig.line(x, y)

fig.x_range = Range1d(1, 10, bounds=(0, 10))
fig.y_range = Range1d(1, 10, bounds=(0, 10))

wheel = fig.select_one(WheelZoomTool)
wheel.zoom_on_axis = False
wheel.maintain_focus = False

show(fig) # dragging up or down against the vertical limits does nothing, expected behavior.

fig2 = figure(y_axis_type='log')
fig2.line(x, y)

fig2.x_range = Range1d(1, 10, bounds=(0, 10))
fig2.y_range = Range1d(1, 10, bounds=(0, 10))

wheel2 = fig2.select_one(WheelZoomTool)
wheel2.zoom_on_axis = False
wheel2.maintain_focus = False

show(fig2) # dragging vertically against the y limits zooms in on the y axis
```


### Stack traceback or browser console output

_No response_

### Screenshots

[Screencast from 2023-10-14 03-50-44.webm](https://github.com/bokeh/bokeh/assets/83480577/ff27dd23-972f-4ce2-b25f-ce831acfde0f)

user: wx4stg
issue_13439: state: open
title: Add support for `ColumnarDataSource.inspection_policy`
body: Early work in progress attempt to unify behavior of selections and inspections.

fixes #13388

user: mattpap
issue_13436: state: open
title: [BUG] Gridplot figure does not render
body: ref: https://discourse.bokeh.org/t/bokeh-3-1-1-does-not-render-any-more-in-jupyter-notebook/10929

### Software versions

Python version      :  3.8.17 (default, Jul  5 2023, 21:04:15) 
IPython version     :  8.12.2
Tornado version     :  6.3.2
Bokeh version       :  3.1.1
BokehJS static path :  /home/johann/anaconda3/envs/sipfront/lib/python3.8/site-packages/bokeh/server/static
node.js version     :  (not installed)
npm version         :  (not installed)
Operating system    :  Linux-5.15.0-84-generic-x86_64-with-glibc2.17

### Browser name and version

Firefox 118.0.2

### Jupyter notebook / Jupyter Lab version

notebook 6.5.4 IPython 8.12.2 jupyter 1.0.0 3.0.7

### Expected behavior

The code below renders fine with Bokeh 2.4.3, notebook 6.5.3 and Ipython 8.11.0

The same code does not render anything at all with Bokeh 3.1.1, notebook 6.5.4, Ipython 8.12.2 and jupyter 1.0.0 and jupyter_bokeh 3.0.7 installed.

It should render the graph, obviously. 



### Observed behavior

This shows the “BokehJS 3.1.1 successfully loaded.” message but no other output whatsoever.

The Javascript console shows the following error message for the tab:

```
Uncaught (in promise) TypeError: this.plot_view.state is undefined
    connect_signals https://cdn.bokeh.org/bokeh/release/bokeh-3.1.1.min.js:648
    build_views https://cdn.bokeh.org/bokeh/release/bokeh-3.1.1.min.js:224
```


### Example code

```Python
import bokeh
print("bokeh", bokeh.__version__)
import bokeh.io
import bokeh.plotting
import numpy as np
import notebook, IPython, jupyter, jupyter_bokeh
print("notebook", notebook.__version__, "IPython", IPython.__version__, "jupyter", jupyter.__version__, jupyter_bokeh.__version__)
bokeh.io.output_notebook()

x=np.arange(100)
y1=np.random.randn(100)
y2=np.random.randn(100)
source = bokeh.models.ColumnDataSource(data=dict(x=x,y1=y1,y2=y2))
xdr = bokeh.models.Range1d(start=0, end=99)
p1 = bokeh.plotting.figure(height=100, width=600,tools="xpan,box_zoom,wheel_zoom,xbox_select,reset,undo", toolbar_location="above",x_axis_location="above", x_range=xdr)
p1.line('x', "y1", source=source)
p2 = bokeh.plotting.figure(height=100, width=600,tools="", toolbar_location=None,x_axis_location="below", x_range=xdr)
p2.line('x', "y2", source=source)
fig=bokeh.layouts.gridplot([p1,p2], ncols=1)
bokeh.plotting.show(fig)
```
```


### Stack traceback or browser console output

See https://discourse.bokeh.org/t/bokeh-3-1-1-does-not-render-any-more-in-jupyter-notebook/10929

### Screenshots

_No response_
user: johann-petrak
issue_13434: state: open
title: [FEATURE] Have FixedTicker honor desired_num_ticks
body: ### Problem description

I have custom x-axis labels per irregular intervals (details: numeric trade time offsets, with non-trading time periods, which is irregularly organized in history, stripped off the timeline), the best option so far as I know, is to use `FixedTicker` on computed boundary x-values labeled with natural timestamp string, e.g. 

```log
2013-02-20 09:00:00	0.0
2013-02-20 09:30:00	1.0
2013-02-20 10:00:00	2.0
2013-02-20 10:30:00	2.5
2013-02-20 11:00:00	3.5
2013-02-20 13:30:00	4.5
2013-02-20 14:00:00	5.5
2013-02-20 14:30:00	6.5
2013-02-21 09:00:00	7.5
2013-02-21 09:30:00	8.5
2013-02-21 10:00:00	9.5
2013-02-21 10:30:00	10.0
2013-02-21 11:00:00	11.0
2013-02-21 13:30:00	12.0
2013-02-21 14:00:00	13.0
2013-02-21 14:30:00	14.0
```

The problem is when not zoomed-in enough, the labels collapse into each others (even after rotated):

![image](https://github.com/bokeh/bokeh/assets/15646573/9bbb38c6-0d1f-4e37-a886-7b3c3ec48264)


### Feature description

At a glance, I guess for there being the code:

https://github.com/bokeh/bokeh/blob/branch-3.4/bokehjs/src/lib/models/tickers/fixed_ticker.ts#L31-L36

```ts
  override get_ticks_no_defaults(_data_low: number, _data_high: number, _cross_loc: number, _desired_n_ticks: number): TickSpec<number> {
    return {
      major: [...this.ticks],
      minor: [...this.minor_ticks],
    }
  }
```

If `this.ticks/this.minor_ticks` are down-sampled according to `_desired_n_ticks`, can my problem get just solved?


### Potential alternatives

idk.

### Additional information

_No response_
user: complyue
issue_13425: state: open
title: Create wiki page for release process
body: @bryevdv has just run through the release process with @Azaya89 and myself. Whilst the process is fresh in our minds, I will create a wiki page for it that we can all edit.
user: ianthomas23
issue_13423: state: open
title: Upgrade slickgrid to version 5.x
body: The new version includes:

* Migrated to TypeScript build which also provides Types (d.ts)
* ES6 / ESM support while still providing standalone scripts (IIFE)
* New Modern Alpine Theme (inspired by Ag-Grid) that can be customized via SASS and/or CSS variables
* Replaced (removed) all images with a small subset of SVGs that can also be colorized via pure CSS

See [here](https://github.com/6pac/SlickGrid/wiki/Major-version-5.0-%E2%80%90-ES6-ESM-and-TypeScript-Support) for details.

This should allow us to drop our vendored version of slickgrid, custom typings and possibly resolve quite a few issues with the existing version (e.g. layout complications as showed in issue #13264). Also given the upcoming round of CSS theming work, having CSS variable support in slickgrid should allow for easier integration.
user: mattpap
labels: dependencies: Pull requests that update a dependency file
issue_13411: state: open
title: Establish a policy for retiring validation error/warning codes
body: As demonstrated in PR #13204, validation error/warnings codes can get obsolete and unused. We need to decide what to do with them. The simplest would be to just remove them, but that's a backwards incompatible change, so we may want to do that in stages and remove only in the next major release.
user: mattpap
issue_13396: state: open
title: 13387 deployment scenario improvements
body: This PR adds a new section to the "Deployment scenario" section of the user guide, adding detail about configuring Bokeh server for integration into other web applications, and linking out to other relevant sections of the docs.

All feedback & suggestions for revision are welcome.

- [ ] issues: fixes #13387
- [ ] tests added / passed: Passing all pre-commit hooks

user: wswope-etiometry
issue_13395: state: open
title: Officially support callable objects for notebook app embed
body: ref: https://discourse.bokeh.org/t/live-plot-with-interactivity/10890/6?u=bryan

While answering the above user question, it occurred to me that callable objects might be a much simpler/more straightforward way to express apps in notebooks that does not involve nested functions (and even potentially having to resort to `nonlocal` as in that question). 

The existing code want's to "tighten up" the types for `show`:

https://github.com/bokeh/bokeh/blob/bb1582ab7be57ac53baed5edae9ab28db4fe9495/src/bokeh/io/showing.py#L148

But I now think we should *not* do that, or at least not in any way that precludes callable objects, and explicitly support (test) with callable objects like the code in the referenced discussion, and even update the example to use this method. 

We could potentially detect classes with a `__call__` and try to auto-instantiate a default instance in that case, but I am only +0 on that I think. 
user: bryevdv
issue_13388: state: open
title: [BUG] TapTool inspection doesn't find indices when used on CDSView
body: ### Software versions

Python version      :  3.10.5 (tags/v3.10.5:f377153, Jun  6 2022, 16:14:13) [MSC v.1929 64 bit (AMD64)] 
IPython version     :  (not installed)
Tornado version     :  6.2
Bokeh version       :  3.2.2
BokehJS static path :  C:\Users\kadek\Desktop\kod\bokeh_playground\venv\lib\site-packages\bokeh\server\static
node.js version     :  (not installed)
npm version         :  (not installed)
Operating system    :  Windows-10-10.0.19045-SP0 

### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

When using TapTool on a glyph that uses CDSView with GroupFilter, the callback object's inspected.indices should be populated with index of the tapped point.

### Observed behavior

The inspected.indices are populated only when taping points falling into the last filter category.

### Example code

```Python
from bokeh.layouts import column
from bokeh.plotting import figure, curdoc
from bokeh.models import ColumnDataSource, TapTool, CustomJS, CDSView, GroupFilter

categories = ["a", "b"]
data = {
	"x": [1, 2],
	"y": [1, 2],
	"z": categories
}
source = ColumnDataSource(data=data)

p = figure(width=300, height=300, tools="pan,reset,save")
p.add_tools(TapTool(
    description="Tap",
    behavior="inspect",
    callback=CustomJS(
        code="""
        	console.log(cb_data.source.inspected.indices);
        """,
    ),
))
for category in categories:
	view = CDSView(filter=GroupFilter("z", category))
	p.scatter(source=source, view=view)

doc = curdoc()
doc.add_root(column(p))
```

I run this with `bokeh serve .\modifiers.py --show`

When in browser, tap on the top right point and you will see `[1]` in browser console.
Tap on the bottom left point and you will see `[]` in browser console.

### Stack traceback or browser console output

_No response_

### Screenshots

![image](https://github.com/bokeh/bokeh/assets/2953645/36346174-d20b-494d-b641-5c97e84d1823)

user: Kadek
issue_13387: state: open
title: [FEATURE] Improving the "Deployment scenario" section of the user guide
body: ### Problem description

This is a tracking and feedback issue for a documentation PR that I'm planning to submit. 

Currently, the "Deployment scenario" section of the user guide does not cover the `server_document` and `server_session` methods used to embed Bokeh server views into other web applications.

Additionally, my naive understanding is that BokehJS currently does not forward Authorization headers when sending the initial HTTP(S) request to set up its websocket connection to Bokeh server. If this is correct, it would be extremely helpful to note that Bokeh server's websocket endpoint must be exempted by auth modules implemented per the "Authentication" section of the "Deployment scenario" page. (I'm not much of a frontend dev and I have limited experience with websockets - apologies if I'm off-base here.)

### Feature description

I have written a documentation subsection covering the two embedding methods mentioned above.

If anyone has bandwidth or past experience to confirm the websocket + auth module issue in the next few days, I will include a few lines regarding that limitation in the PR linked to this issue - otherwise, I'll set it aside for now. The most relevant sections of the Bokeh code are under `bokehjs/src/lib/client`, which appear to call out to this websocket library: https://github.com/websockets/ws/.

### Potential alternatives

n/a

### Additional information

Original discourse thread here: https://discourse.bokeh.org/t/improving-the-bokeh-server-deployment-scenario-section-of-the-user-guide/10852.
user: wswope-etiometry
issue_13384: state: open
title: [BUG] Empty plots when gridplot nested inside Tab within row
body: ### Software versions

Python version      :  3.11.2
IPython version     :  (not installed)                                                                               
Tornado version     :  6.3.3                                                                                         
Bokeh version       :  3.2.2                                                                                         
BokehJS static path :  ...\.venv\Lib\site-packages\bokeh\server\static
node.js version     :  (not installed)                                                                               
npm version         :  (not installed)                                                                               
Operating system    :  Windows-10-10.0.19045-SP0


### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

Multiple plots are arranged in a gridplot.
The gridplot is placed inside a column, together with another plot.
The column is placed inside a TabPanel/Tabs.
The tab is placed inside a column/row/layout together with another plot.
All plots are visible.


### Observed behavior

The plots inside the gridplot are empty as soon as the Tab is nested inside another row/column/layout. 
It works, if further columns/rows are used instead of gridplot.

### Example code

```Python
from bokeh.plotting import figure, show
from bokeh.layouts import gridplot
from bokeh.models import TabPanel, Tabs
from bokeh.layouts import row, column

x = list(range(11))
y0 = x
y1 = [10 - i for i in x]
y2 = [abs(i - 5) for i in x]

# create three plots
s1 = figure(background_fill_color="#fafafa")
s1.circle(x, y0, size=12, alpha=0.8, color="#53777a")

s2 = figure(background_fill_color="#fafafa")
s2.triangle(x, y1, size=12, alpha=0.8, color="#c02942")

s3 = figure(background_fill_color="#fafafa")
s3.square(x, y2, size=12, alpha=0.8, color="#d95b43")

# make a grid
grid = gridplot([s2, s3], width=250, height=250, ncols=2)

test_col = column(children=[s1, grid])
# show(test_col)                                            #  -> All plots visible
panel = TabPanel(child=test_col, title="Tab with grid")
tabs = Tabs(tabs=[panel])
#show(tabs)                                                 #  -> All plots visible (inside tab)
test_row = row(tabs)
#show(test_row)                                             # -> Only s1 visible, s2 and s3 buggy

combined = column(s1, row(s2, s3))                          # using column and rows instead of gridplot
#show(combined)                                             #  -> All plots visible
panel = TabPanel(child=combined, title="Tab with col/row")
tabs = Tabs(tabs=[panel])
#show(tabs)                                                 #  -> All plots visible (inside tab)
test_row = row(tabs)
show(test_row)                                              #  -> All plots visible (inside tab)
```


### Stack traceback or browser console output

_No response_

### Screenshots

![image](https://github.com/bokeh/bokeh/assets/66748564/578b00a4-bd68-4d89-8e83-482716ecd88f)

user: fxwest
issue_13381: state: open
title: Document new-style (ESM) JS callbacks
body: This was introduced in PR #12812, which only added docstrings for documentation.
user: mattpap
issue_13375: state: open
title: [FEATURE] Documentation and examples for embedding plots inside tooltips
body: ### Problem description

(This issue is a follow up to [this discussion on the project's discourse](https://discourse.bokeh.org/t/embedding-a-bokeh-plot-inside-a-tooltip/10839)).

It is often useful to embed a plot/layout inside the tooltips (e.g. in a map visualization where you can show a bar plot for each state when hovered over) and Bokeh provides a great way to accomplish this. But currently this feature is very under-documented and there are not many implementation examples. The support for this feature was added in #11165 but many of the added classes (such as `Template`, `ToggleGroup`, `ValueRef`, etc.) don't have enough documentation. The [code here](https://docs.bokeh.org/en/3.2.2/docs/examples/plotting/periodic_shells.html) shows an example of this feature and is fairly easy to follow. It would be great if we can have more examples like this and can add documentation for this feature.

### Feature description

Increased documentation for adding plots/layouts inside tooltips.

### Potential alternatives

N/A

### Additional information

Some examples of plots inside tooltips:

Bokeh example:
https://docs.bokeh.org/en/3.2.2/docs/examples/plotting/periodic_shells.html

Bokeh map tooltip example:
<img width="1029" alt="Screen Shot 2023-09-12 at 1 05 35 PM" src="https://github.com/bokeh/bokeh/assets/47955537/59bc73a3-fd0c-4d64-82f3-bf44cea596c4">

from [fivethirtyeight](https://projects.fivethirtyeight.com/redistricting-2022-maps/):
<img width="900" alt="Screen Shot 2023-09-12 at 1 01 37 PM" src="https://github.com/bokeh/bokeh/assets/47955537/1f4e5d73-77df-455b-b152-686e552b4fab">

user: nmasnadi
issue_13364: state: open
title: Add more examples in the gallery
body: ### Problem description

During the course of the project on *[Fundamentals of data visualization](https://github.com/bokeh/dataviz-fundamentals/tree/main)*, @bryevdv suggested we add some of the plots to our gallery examples and docs. A good example to add would be the `SLOPE GRAPH` found in this [notebook](https://github.com/bokeh/dataviz-fundamentals/blob/main/notebooks/06-Visualizing-associations-among-variables.ipynb). 

### Feature description

Here's the image of the plot:![image](https://github.com/bokeh/bokeh/assets/99359668/2424bcb6-0eab-480b-a61c-13eef813abe7)

Since the [csv file](https://github.com/bokeh/dataviz-fundamentals/blob/main/data/csv_files/CO2_emissions.csv) for this plot is a small file (~ 300kb), I suggest we also add it as part of the source files as well.

@bokeh/dev 
user: Azaya89
issue_13354: state: open
title: [FEATURE] Generalizing `image_stack` for deep categorical inspection
body: ### Problem description

The new `image_stack` glyph allows for client-side color mixing, replacing the sort of operation that tools such as datashader would do on the server-side for visualizing categorical data (an exciting new feature by the way!).

One of the exciting features of being able to do this client-side is that you can use the hover tool to inspect image stacks. The work in https://github.com/bokeh/bokeh/pull/13193 address this partially with further (related!) discussion in https://github.com/bokeh/bokeh/issues/13200. However, even if both these suggestions were implemented, I think some of the potential power of image stack inspection would still not be realized. This issue outlines an ambitious vision for how I can imagine image stacks working.

Here is a mockup I've made for my idea hover inspection based on a real example (ship traffic around Vancouver):

<img width="1162" alt="image" src="https://github.com/bokeh/bokeh/assets/890576/a0874954-944e-4059-8d61-d9df5260af4f">

This would be an `image_stack` on top of a tile source where 'Cargo' and 'Passenger' are ship categories. The color mixing is based on the number of ships (of different categories) aggregated in a given pixel (the 'count' in datashader terminology). However, the data has a lot more structure than this! We also have the ships' name, tonnage, length and heading and what I think would be really useful is to see a hover tool with all this information for all the relevant categories at the cursor position (in practice this would be showing the output of a filter, e.g. the biggest ship by category, or some statistics aggregated over all the ships within each category).

To make this work, you would need to allow image stacks to be composed of an arbitrary number (`Z`) of `X x Y x C` ndarrays (where `X` and `Y` are the x- and y-resolutions and `C` is the number of categories). Of these `Z` ndarrays, only one would be used for color mixing and the rest are used to provide hover information. In the example above, your `ndarrays` would be for `name`, `tonnage`, `length` and `heading` (note the fact that dtypes can vary). 

Then you would need a hover tool with the ability to present this information: the default would hopefully be something like the mockup but you would want the ability to customize the format.


### Feature description

This feature request builds on top of the one made in https://github.com/bokeh/bokeh/issues/13200: in fact, that request (preserving the categorical information) is a prerequisite for what is suggested here.

In addition to preserving the category labels, you now also have the various dimension labels (e.g. you could pass in your ndarrays as a dictionary where the string keys are the names) e.g. `{'name:<string array>, 'tonnage': <float array>, ...}`

Finally, I would hope for a nice default hover tool behavior along the lines of the mockup (with setting to truncate when the list gets too long). However, the necessary extensions to the hover tool format specification should also be supported to allow custom hover tools that can present the categories, dimension labels and corresponding data in whatever format they desire.

Together with color mixing, I think such a system would be a very powerful way to explore categorical data in image stacks!

### Potential alternatives

For images, you could make a hover tool that flattens everything out into a list which becomes quite verbose and hard to read:

```
Cargo ship name: NORD VALIANT
Cargo ship tonnage: ...
Cargo ship length: ...
Cargo ship heading: ...
Passenger ship name: ORIENTAL STAR
Passenger ship tonnage: ...
Passenger ship length: ...
Passenger ship heading: ...
```

However, the goal would be to make image stacks work nicely.

*Edit*: Another potential alternative would be a hover tool that can communicate where you are hovering to the server and that can then display whatever information the server then computes in response (i.e. a dynamic version of the hover tool).


### Additional information

_No response_
user: jlstevens
issue_13350: state: open
title: Possibility to reset plot from Python
body: ### Problem description

Would like to be able to reset plot like done with the toolbar reset button from Python

Discuss on discord few years ago suggested Javascript alternative which I cannot make work (see below).
However still think having a pure Python method would be a nice alternative as it is not fully clear to me how I could reset one single specific plot on a multi-plot page using that alternative.

### Feature description

Function on a python plot which would trigger a plot view reset

### Potential alternatives

There is a discussion on discord on this: https://discourse.bokeh.org/t/how-to-reset-figure-directly-from-python-code-like-the-resettool-button/4787

Solution provided in discord does not work for me today.

With bokeh  2.4.3
document.querySelectorAll('.bk-tool-icon-reset[title="Reset"]') return correct list of reset tools items
However nothing happen on d.click()

With  bokeh 3.2.0
document.querySelectorAll('.bk-tool-icon-reset[title="Reset"]') return nothing
Have limited knowledge of javascript, but can see title parameter has been removed so I tried
document.querySelectorAll('.bk-tool-icon-reset'), still the same return nothing

### Additional information

Need for this reset option is linked to this issue when using bokeh plots in panel Tabs widget, I reported here:
https://github.com/holoviz/panel/issues/5298

I am looking for ways to mitigate the issue.
user: RaphaelRosset
issue_13346: state: open
title: Finalize sub-plot API and add documentation
body: Early work in progress. First I need to finalize PR #12068 and then I will complete the documentation here.

- [ ] fixes #12073
user: mattpap
issue_13344: state: open
title: Replace `Whisker` annotation with a glyph
body: This is a proof of concept and a breaking change. This can be easily made into a non-breaking change by either naming the glyph differently or introducing two glyphs (`{H,V}Whisker`), similarly to what was done with `Span` annotation vs. span-like glyphs (PR #12677). The functionality is preserved except for screen units. Those can or will be possible to reproduce with sub-coordinates (a part of my current CZI work), though I'm not sure about usefulness of that. I also added `figure().whisker()` for API completeness.

![image](https://github.com/bokeh/bokeh/assets/27475/fac4784b-22b7-4f87-b146-00a22682a06a)

addresses #9955

user: mattpap
issue_13340: state: open
title: [BUG] DataTable columns collapse onto each other when layout is modified
body: ### Software versions

Python version      :  3.9.17 (main, Jun  6 2023, 20:11:21) 
IPython version     :  8.14.0
Tornado version     :  6.3.2
Bokeh version       :  3.2.2
BokehJS static path :  <radacted>/.venv/lib/python3.9/site-packages/bokeh/server/static
node.js version     :  (not installed)
npm version         :  (not installed)
Operating system    :  Linux-5.15.90.1-microsoft-standard-WSL2-x86_64-with-glibc2.35

### Browser name and version

Firefox and Chrome

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

I generate an App where I want to let the user decide what plot he wants to see from a table.

When the plot is replaced, all the unrelated layouts/children should not be affected.

### Observed behavior

The table, that is part of the view starts behaving weirdly: the columns collapse on one another.

I am not certain, if this is me doing something unreasonable?

### Example code

```Python
import bokeh.models
import bokeh.layouts
import bokeh.plotting
import bokeh.io

buttons = bokeh.models.RadioButtonGroup(
    labels=["A", "B"], active=0, sizing_mode="stretch_both"
)

source = bokeh.models.ColumnDataSource(
    {
        "A": ["seize", "cereal", "notebook", "translate"],
        "B": ["talented", "bang", "seed", "occupation"],
        "C": ["price", "preach", "leave", "dance"],
        "D": ["general", "endure", "monster", "divorce"],
        "E": [0, 1, 2, 3],
        "F": [0, 1, 4, 9],
    }
)

A = bokeh.layouts.column(
    [
        bokeh.models.Spacer(sizing_mode="stretch_height"),
        bokeh.models.Div(
            text="I am showing something related to A!",
            styles={
                "align": "center",
                "text-align": "center",
                # "background-color": "grey",
            },
            sizing_mode="stretch_width",
            align="center",
        ),
        bokeh.models.Spacer(sizing_mode="stretch_height"),
    ],
    sizing_mode="stretch_both",
    # styles={"background-color": "blue"},
)

B = bokeh.plotting.figure(sizing_mode="stretch_both")
B.line("E", "F", source=source)

table = bokeh.models.DataTable(
    sizing_mode="stretch_both",
    source=source,
    columns=[bokeh.models.TableColumn(field=c, title=c) for c in source.data.keys()],
)

layout = bokeh.layouts.row(
    [buttons, bokeh.layouts.column([A, table], sizing_mode="stretch_both")],
    sizing_mode="stretch_both",
)

custom_reaction = bokeh.models.CustomJS(
    args={"view_A": A, "view_B": B, "buttons": buttons, "layout": layout.children[1]},
    code="""
    console.log(buttons.labels[buttons.active])
    if (buttons.active == 0) {
        layout.children = [view_A, layout.children[1]];
    } else {
        layout.children = [view_B, layout.children[1]];
    }
    """,
)
buttons.js_on_change("active", custom_reaction)

bokeh.io.output_file("myapp.html", title="MyApp")
bokeh.io.save(layout)
```


### Stack traceback or browser console output

_No response_

### Screenshots

Left is before Button "B" is pushed and the layout is modified. Right after.
Pushing the Button "A" does not return the table into its original state.

![grafik](https://github.com/bokeh/bokeh/assets/5512286/46fabf26-5ec3-434b-85b0-636dc2a4f8a7)
user: thatguy7
issue_13336: state: open
title: Add violin plot example to statistical user guide section
body: As part of @Azaya89's internship, he reproduced a violin plot from *Fundamentals of Data Visualization*. Previously these were somewhat of a pain to generate with patch or polygon glyphs. However, they are not actually so bad if using the newer `harea` glyph. (The one thing given up by by not using patches/polygons is an outline, however)

Propose to add a new section and example to demonstrate a violin plot using `harea` and also add a simple `offset` convenience function to assist with generating categorical offset coordinates (useful at least until sub-coordinates for these plots becomes an option, but also even afterwards).
user: bryevdv
issue_13335: state: open
title: Minify WebGL shaders
body: Currently the WebGL shaders (`*.vert` and `*.frag` files) are not minified when they are bundled into the minified bundles such as `bokeh-gl.min.js`. They should be minified to reduce bundle and download size.

Things to do:
1. Remove comments
2. Remove unnecessary whitespace
3. Rename temporary variables to have shorter names (optional)

Items 1 and 2 could be done by someone without any GLSL knowledge. Item 3 needs some knowledge of what are and aren't GLSL keywords which is why I have labelled it optional.

This could be done using a third-party JavaScript library, or we could roll our own alongside similar code in the `bokehjs/make/tasks` directory.
user: ianthomas23
issue_13332: state: open
title: Make `GlyphView.mask_data()` always narrow indices to the viewport
body: Previously `mask_data()` usually returned all (subset) indices and only give non-trivial results for a handful of glyphs. This PR makes `mask_data()` always narrow indices to view viewport (and utilize the spatial index). There's a handful of glyphs that require special behavior, because e.g. their `_render()` method doesn't work will with non-contiguous indices. This is in preparation to fix point (1) from https://github.com/holoviz/holoviews/pull/5840#issuecomment-1663504592.
user: mattpap
issue_13325: state: open
title: [FEATURE] Implement `pan_together` option for `PanTool` analogous to `zoom_together` for `WheelZoomTool`
body: ### Problem description

On figures with multiple x- or y-axes, the PanTool operates all x- or y-axes at the same time, maintaining the alignment of the axes. However, in our use we often want to independently pan one y-axis with respect to another, for example. We are actually able to achieve this behavior in a round-about way using the `WheelZoomTool` configured with `zoom_together='none'` by zooming out with the cursor at one position on the axis and then zooming in with the cursor at another point on the same axis. The different behavior of the pan and zoom tools in this case is unexpected by users interacting with the figure.

### Feature description

Suitably configured via a parameter analogous to `zoom_together` (`pan_together`?), panning on one axis will pan that axis only and leave others unaffected. Presumably panning in the area of the figure would maintain the current behavior. Unlike with `zoom_together`, the "cross" option would not make sense with `pan_together`.

### Potential alternatives

A workaround is to use the `WheelZoomTool` as discussed. Not sure of other alternatives.

### Additional information

Looking briefly at the code for `WheelZoomTool`, it seems like similar (though simpler, because there is no zooming) logic could be applied to the operation of the `PanTool`.
user: ryanolf-tb
issue_13320: state: open
title: [BUG] Cross origin iframe jumps when a DataTable row is selected
body: ### Software versions

Python version      :  3.11.4 (main, Aug  7 2023, 10:37:01) [Clang 14.0.3 (clang-1403.0.22.14.1)]
IPython version     :  8.14.0
Tornado version     :  6.3.2
Bokeh version       :  3.2.1
BokehJS static path :  /Users/username/.pyenv/versions/3.11.4/envs/ah-premium/lib/python3.11/site-packages/bokeh/server/static
node.js version     :  v20.5.0
npm version         :  9.8.0
Operating system    :  macOS-13.5-arm64-arm-64bit

### Browser name and version

Firefox 116.0.2 Chrome 115.0.5790.170

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

When a Bokeh Datatable - served from a different origin - is embedded in an iframe, selecting a row should not scroll the the iframe

### Observed behavior

On selecting a row, if the datatable header is not visible on screen, the iframe jumps up

### Example code

```Python
from datetime import date
from random import randint

from bokeh.io import show
from bokeh.models import ColumnDataSource, DataTable, DateFormatter, TableColumn

N = 28
data = dict(
        dates=[date(2014, 3, i+1) for i in range(N)],
        downloads=[randint(0, 100) for i in range(N)],
    )
source = ColumnDataSource(data)

columns = [
        TableColumn(field="dates", title="Date", formatter=DateFormatter()),
        TableColumn(field="downloads", title="Downloads"),
    ]
data_table = DataTable(source=source, columns=columns,  height=N*28,
                                     scroll_to_selection=False,
                                     sizing_mode='stretch_width',)
# Stretch width just so the table is easier to see.
# Scroll to selection true vs false has no effect.

save(data_table, title='test', filename='testtable.html')

# Now embed this in an iframe like (900px just makes it easier to see)
# <iframe src="https://testtable.pages.dev/testtable" style="width:100%;height:900px;"></iframe>
# The URL points to the example above
```


### Stack traceback or browser console output

_No response_

### Screenshots

![bokeh issue(1)](https://github.com/bokeh/bokeh/assets/39788096/1b175047-5df2-451a-889d-e0753ba2f3d4)

user: woodsy-sounding-wilful-sapwood
issue_13316: state: open
title: Add IndexedStackColorMapper for ImageStack glyph
body: This is an early-stage WIP to add an `IndexedStackColorMapper` which is an alternative use of `ImageStack` glyph that color maps and displays a single 2D array of the 3D `ImageStack` data.

The data displayed is controlled by an `index` property, which will cause `data[:, :, index]` to be displayed. The actual colormapping is controlled by a contained `single_color_mapper` so that it can be linear, logarithmic or eq_hist. There is a boolean property `map_all` (maybe should be `scan_all` but I don't like either much) that controls whether the colormap limits/properties are calculated from the all of the 3D data or just the 2D data displayed.

Example script:
```python
from bokeh.models import IndexedStackColorMapper, LinearColorMapper, Slider, Toggle
from bokeh.plotting import column, figure, row, show
import numpy as np

ny, nx, nstack = 5, 5, 4
rng = np.random.default_rng(22529)
data = rng.random((ny, ny, nstack))
for k in range(nstack):
    data[:, :, k] += k*0.2

p = figure(width=500, height=400,
           title=f"ImageStack of shape {data.shape} with IndexedStackColorMapper")

# Do not want palette on both LinearColorMapper and IndexedStackColorMapper as only one used ???
single = LinearColorMapper(palette='Spectral10')
color_mapper = IndexedStackColorMapper(single_color_mapper=single, palette='Spectral10')
im = p.image_stack(image=[data], x=0, y=0, dw=1, dh=1, color_mapper=color_mapper)

cbar = im.construct_color_bar()
p.add_layout(cbar, "right")

slider = Slider(start=0, end=nstack-1, value=0, step=1, title="Index")
slider.js_link("value", color_mapper, "index")

toggle = Toggle(label="map_all")
toggle.js_link("active", color_mapper, "map_all")

show(column(p, row(slider, toggle)))
```
and video of this:
https://github.com/bokeh/bokeh/assets/580326/ccfcc2cf-7c58-425e-9023-2019bf4bec9c

One problem with this approach is that both the `LinearColorMapper` and `IndexedStackColorMapper` must have a `palette` specified (`None` is not allowed for `ColorMapper`s) but only one is used.

Also, I have had to reimplement part of `ContinuousColorMapper._v_compute` within `IndexedStackColorMapper` when it scans all of the data but only displays one 2D slice of it. Probably a better approach would be a refactor of the various `_v_compute` functions to accept the scanned data rather than scan it themselves.
user: ianthomas23
issue_13302: state: open
title: [BUG] Extra y range does not scale independently of default axis
body: ### Software versions

Python version      :  3.10.8 (tags/v3.10.8:aaaf517, Oct 11 2022, 16:50:30) [MSC v.1933 64 bit (AMD64)]
IPython version     :  (not installed)
Tornado version     :  6.2
Bokeh version       :  3.2.1
node.js version     :  v16.15.0
npm version         :  8.5.5
Operating system    :  Windows-10-10.0.19045-SP0


### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

Note: This may be related to #13297 

In a twin axis setup each of the axis should scale independently based on the data that is assigned to the respective axis. 

### Observed behavior

If no y_range is set explicitly both axis will be scaled the same despite the specific data <-> axis assignment. 

### Example code

```Python
from bokeh.io import curdoc
from numpy import arange, linspace, pi, sin

from bokeh.models import (CustomJS, LinearAxis, Range1d, Select,
                          WheelZoomTool, ZoomInTool, ZoomOutTool, DataRange1d, Button)
from bokeh.palettes import Sunset6
from bokeh.plotting import figure, show

x = arange(-2*pi, 2*pi, 0.2)
y = sin(x)
y2 = linspace(0, 100, len(x))

blue, red = Sunset6[2], Sunset6[5]

p = figure() # figure(y_range=(-1, 1)) providing y_range explicitly yields the desired result
p.background_fill_color = "#fafafa"
p.axis.axis_label = "light blue circles"
p.axis.axis_label_text_color = blue

p.extra_y_ranges['foo'] = DataRange1d()

ax2 = LinearAxis(
    axis_label="red circles",
    y_range_name="foo",
)
ax2.axis_label_text_color = red
p.add_layout(ax2, 'right')


p.scatter(x, y, line_color="black", fill_color=blue, size=12)
p.scatter(x, y2, color=red, size=8, y_range_name="foo", )


doc = curdoc()
doc.add_root(p)
```


### Stack traceback or browser console output

_No response_

### Screenshots

![image](https://github.com/bokeh/bokeh/assets/71090451/a28596bb-d9b1-4e58-bea3-f9984d9ec2ac)
user: muendlein
issue_13300: state: open
title: [BUG] Inconsistent behavior when overriding the default y_range_name
body: ### Software versions

Python version      :  3.10.8 (tags/v3.10.8:aaaf517, Oct 11 2022, 16:50:30) [MSC v.1933 64 bit (AMD64)]
IPython version     :  (not installed)
Tornado version     :  6.2
Bokeh version       :  3.2.1
node.js version     :  v16.15.0
npm version         :  8.5.5
Operating system    :  Windows-10-10.0.19045-SP0


### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

Overriding the default y_range_name ("default") should be possible and yield a consistent result or be disable entirely. Note: this issue may also be labelled as discussion/feature.

### Observed behavior

When trying to assign a custom y_range_name the new name is appended in a list which is rather unexpected (before `'default'`, after `['default', 'foo']`). Additionally a E-1020 (BAD_EXTRA_RANGE_NAME) error is indicated in the log which begs the question whether assigning a custom y_range_name should even be possible.
```
p = figure(x_range=(-2*pi, 2*pi), y_range=(-1, 1))
p.yaxis.y_range_name = "foo"
p.scatter(x, y, line_color="black", size=12, y_range_name="foo")
```
If the default string "default" is used instead of "foo", no error will be thrown and the code executes just fine.
```
p = figure(x_range=(-2*pi, 2*pi), y_range=(-1, 1))
p.yaxis.y_range_name = "default"
p.scatter(x, y, line_color="black", size=12, y_range_name="default")
```

_Why would one even want to override the default y_range_name?_
When dealing with multiple (extra) ranges/axis the y_range_name is used to assign data to the corresponding axis. In this context the default axis should be behave the same as any of the extra axis. This especially relevant in a dynamic scenario where it might not be clear which axis is the default one.

### Example code

```Python
from bokeh.io import curdoc
from numpy import arange, pi, sin

from bokeh.layouts import column
from bokeh.plotting import figure

x = arange(-2*pi, 2*pi, 0.2)
y = sin(x)

p = figure(x_range=(-2*pi, 2*pi), y_range=(-1, 1))
p.yaxis.y_range_name = "foo" # after: ['default', 'foo']

blue_circles = p.scatter(x, y, line_color="black", size=12, y_range_name="foo")


doc = curdoc()
doc.add_root(column(p))
```


### Stack traceback or browser console output

_No response_

### Screenshots

_No response_
user: muendlein
issue_13299: state: open
title: [BUG] Button icon does not change on update
body: ### Software versions

Python version      :  3.9.16 | packaged by conda-forge | (main, Feb  1 2023, 21:38:11) 
IPython version     :  8.14.0
Tornado version     :  6.3.2
Bokeh version       :  3.2.1
BokehJS static path :  [/Users/ahuang/miniconda3/envs/panel/lib/python3.9/site-packages/bokeh/server/static](https://file+.vscode-resource.vscode-cdn.net/Users/ahuang/miniconda3/envs/panel/lib/python3.9/site-packages/bokeh/server/static)
node.js version     :  v18.15.0
npm version         :  9.5.0
Operating system    :  macOS-13.4-arm64-arm-64bit

### Browser name and version

Google Chrome

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

The icon to update on click, like the label.

### Observed behavior

The icon doesn't change.

### Example code

```Python
from bokeh.plotting import curdoc
from bokeh.layouts import column
from bokeh.models import Button, TablerIcon
from bokeh.io import show

# Function to update the button label
def update_button_label():
    if button.label == "Click Me":
        button.label = "Clicked!"
        button.icon = TablerIcon(icon_name="thumb-down")
    else:
        button.label = "Click Me"
        button.icon = TablerIcon(icon_name="thumb-up")

# Create a Bokeh button
button = Button(label="Click Me", icon=TablerIcon(icon_name="thumb-up"))
button.on_click(update_button_label)

# Create a layout with the button
layout = column(button)

# Add the layout to the current document
curdoc().add_root(layout)
```


### Stack traceback or browser console output

_No response_

### Screenshots

Before click
<img width="104" alt="image" src="https://github.com/bokeh/bokeh/assets/15331990/b3d2a0e2-e5c5-46ea-b87c-49ce65cbc2d6">

After click
<img width="92" alt="image" src="https://github.com/bokeh/bokeh/assets/15331990/f008cbda-1471-4a0f-815c-1d2616eedaf6">

user: ahuang11
issue_13297: state: open
title: [BUG] Extra range not working with DataRange1d
body: ### Software versions

IPython version     :  (not installed)
Tornado version     :  6.2
Bokeh version       :  3.2.1
node.js version     :  v16.15.0
npm version         :  8.5.5
Operating system    :  Windows-10-10.0.19045-SP0


### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

Using extra x/y ranges should work with both DataRange1d and Range1d even if the extra data is added dynamically.

### Observed behavior

When adding the extra data dynamically (i.e. with a button click (see example)), a JS error (`[bokeh] could not set initial ranges`) will be thrown in case of DataRange1d. This does not happen for Range1d with fixed ranges.

### Example code

```Python
from bokeh.io import curdoc
from numpy import arange, linspace, pi, sin

from bokeh.layouts import column
from bokeh.models import (CustomJS, LinearAxis, Range1d, Select,
                          WheelZoomTool, ZoomInTool, ZoomOutTool, DataRange1d, Button)
from bokeh.palettes import Sunset6
from bokeh.plotting import figure, show

x = arange(-2*pi, 2*pi, 0.2)
y = sin(x)
y2 = linspace(0, 100, len(x))

blue, red = Sunset6[2], Sunset6[5]

p = figure(x_range=(-2*pi, 2*pi), y_range=(-1, 1), tools="pan,box_zoom,save,reset")
p.background_fill_color = "#fafafa"
p.axis.axis_label = "light blue circles"
p.axis.axis_label_text_color = blue
p.scatter(x, y, line_color="black", fill_color=blue, size=12)


def add_extra_data():
    p.extra_y_ranges['foo'] = DataRange1d() # Range1d(0, 100) works as intended

    ax2 = LinearAxis(
        axis_label="red circles",
        y_range_name="foo",
    )
    ax2.axis_label_text_color = red
    p.add_layout(ax2, 'right')

    p.scatter(x, y2, color=red, size=8,
                            y_range_name="foo",
                            )


button_add_data = Button(label="Click to add extra data")
button_add_data.on_click(add_extra_data)

doc = curdoc()
doc.add_root(column(button_add_data, p))
```


### Stack traceback or browser console output

_No response_

### Screenshots

_No response_
user: muendlein
issue_13292: state: open
title: Allow to statically type models and properties in bokeh
body: ### Software versions

Python version: 3.11.4
Bokeh version: 3.2.1
mypy version: 1.4.1

### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

I am receiving a stubborn mypy error when setting the font of my plot titles.

### Observed behavior

error: "Property[Any]" has no attribute "text_font"

### Example code

```Python
from bokeh.plotting import figure, show
from typing import Any

plot_settings: dict[str, Any] = {
	'height': 300,
	'toolbar_location': None,
	'width': 300,
	'x_range': (-1., 1.),
	'y_range': (-1., 1.),
}
truth_fig = figure(title='Ground Truth', **plot_settings)
truth_fig.title.text_font = truth_fig.axis.major_label_text_font = 'CMU serif'
plot_settings = {'fill_color': '#ffffff', 'line_color': '#101010', 'x': 0., 'y': 0.}
truth_fig.circle(radius=0.5, **plot_settings)
show(truth_fig)
```


### Stack traceback or browser console output

_No response_

### Screenshots

_No response_
user: lewiswolf
labels: python: Issues that should only require updating Python code
issue_13287: state: open
title: Replace/Improve on "add_layout"
body: I was reminded by https://github.com/bokeh/bokeh/pull/13286 of a recent discussion about `add_layout`. The name `add_layout` is historical baggage that conveys nothing useful to users at best, and is actively confusing at worst. Currently, `add_layout` is used to:

* add axes, usually not in "center"
* add grids, always in "center"
* add legends, usually in 'center"
* add titles, probably never in "center"
* add labels, (AFAIK) always in "center"
* add slopes, always in "center"

The main issue for me is that in full generality, `add_layout` admits more combinations than make sense. It seems that labels will eventually be subsumed by vectorized text glyphs. Should we just make dedicated `figure` methods for adding these objects, e.g. `p.add_axis(...)`, `p.add_legend(...)`, etc? Then the methods could be constrained to parameters that make sense for the specific thing at hand. There are not too many of these, but OTOH `figure` already has very many methods, and maybe there are more annotations/guides in the future. 

cc @bokeh/dev I'd really like some input and ideas about what the best API(s) here could be. 
user: bryevdv
issue_13286: state: open
title: Add support for canvas layouts and sub-plots
body: This is a very early prototype and a lot of work is needed to make this viable. Currently this allows to create single canvas grid plots with multiple `PlotRenderer`s. `PlotRenderer` is an equivalent of `Plot`, but it doesn't participate in CSS layout and doesn't own a canvas. Later `Plot` will be reimplemented using `PlotRenderer`, but for how I'm trying to avoid breaking too many things. UI events and some of the tools already work.

Example. Nothing special for now, except it's a single canvas. I will be updating this as new features are implemented.
![image](https://github.com/bokeh/bokeh/assets/27475/5b143926-6567-4b51-b347-f9457d82c70a)

- [ ] fixes #3821 
- [ ] fixes #5186
- [ ] fixes #5303
- [ ] fixes #7607
- [ ] fixes #12215
- [ ] fixes #13225
user: mattpap
issue_13279: state: open
title: Add support for basic measurement tools
body: Very early prototype. The main goals of this PR are the following:

1. Establish a set of "scalar" glyphs (called shapes), that will be useful as basic building blocks of more complex visuals, but without the heaviness of glyphs and legacy cruft of annotations.
2. Establish a more flexible way of specifying coordinates while migrating to use sub-coordinates (e.g. no `x_units` etc. anymore).

Measurement tools in this PR are a side effect and a practical way of showing those working.

fixes #182

Example:
![image](https://github.com/bokeh/bokeh/assets/27475/ac430777-3010-4562-a43f-1c28cbb4b8b9)

user: mattpap
issue_13268: state: open
title: [BUG] DataTable is unreadable with dark themes (Jupyter-lab/vscode)
body: ### Software versions

Python version      :  3.10.6 (main, May 29 2023, 11:10:38) [GCC 11.3.0]
IPython version     :  8.11.0
Tornado version     :  6.2
Bokeh version       :  3.2.0
BokehJS static path :  /home/jn1861/.local/lib/python3.10/site-packages/bokeh/server/static
node.js version     :  (not installed)
npm version         :  (not installed)
Operating system    :  Linux-5.15.0-76-generic-x86_64-with-glibc2.35


### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

jupyterlab==4.0.3

### Expected behavior

The display of a DataTable within a notebook is readable when using an editor with a dark theme (with Jupyter-lab or within vscode). 

### Observed behavior

The tables are unreadable/hard to read. The underlying theme does not seem have any effect on the tables either (set with `curdoc().theme = 'dark_minimal'`) Happens with both Firefox and Chrome when using Jupyter-lab. I believe https://github.com/bokeh/bokeh/issues/12122 could be the same issue.

The cell background and font color are too similar.

### Example code

```Python
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource, TableColumn, DataTable
from bokeh.io import show
import pandas as pd

df = pd.DataFrame({
    'SubjectID': ['Subject_01','Subject_02','Subject_03'],
})
source = ColumnDataSource(df)
columns = [
    TableColumn(field='SubjectID', title='SubjectID'),
    ]
myTable = DataTable(source=source, columns=columns)
show(myTable)
```


### Stack traceback or browser console output

_No response_

### Screenshots

![image](https://github.com/bokeh/bokeh/assets/7360537/a62603d8-aafc-4921-8d58-88cf01bf2073)

user: nordstroem
issue_13260: state: open
title: Using the locale for Datetime formatting
body: ### Problem description

Hello,

the problem is simple: I don’t live in US or UK as many other Bokeh users. So it would be nice to have access to the locale for the formatting of Datetime data. I’m far from being a specialist so forgive me if this feature is already available and I don’t see it.

A puzzled user.

### Feature description

The locale should override the global Datetime system except if noted.

### Potential alternatives

The people who are not in English-spoken countries have found workarounds but it’s uselessly complicated.

Please, the locale…

### Additional information

The problem explained there:
https://stackoverflow.com/questions/52699706/how-do-i-set-language-of-datetime-axis-in-bokeh
user: Listine
issue_13251: state: open
title: Databricks support
body: ### Problem description

Dear team,

I love using bokeh, but it seems that your native notebook_output() function does not support databricks notebook. However, if you do below and use the plot_bokeh() function instead, it all worked out!!

```python
from bokeh.embed import file_html
from bokeh.resources import CDN 
from IPython.display import display, HTML

def plot_bokeh(p):
    display(HTML(file_html(p, CDN, "temp")))
```

### Feature description

Databricks notebook support

### Potential alternatives

-

### Additional information

_No response_
user: coco90417
issue_13245: state: open
title: Title and tick label on ColorBar [FEATURE] 
body: ### Problem description

In my ongoing [project](https://github.com/bokeh/dataviz-fundamentals)  focused on recreating the plots from the book, [*Fundamentals of data visualization*](https://clauswilke.com/dataviz) by Claus Wilke, I recently attempted to reproduce the heatmap featured in Chapter 6, specifically figure 6.14 and 6.15 [here](https://clauswilke.com/dataviz/visualizing-amounts.html#dot-plots-and-heatmaps). However, I discovered that the `ColorBar` function lacks the necessary parameter to adjust the tick location or move the title label to the center. 

### Feature description

I would suggest adding the option to position the ticks below the color bar when it is placed above the plot. This would provide greater flexibility in dealing with color bars in general and also help me more closely align with the book's representation.

### Potential alternatives

No other alternative seems suitable.

### Additional information

Here's my copy of the figure 6.15 found in the book:

![heatmap](https://github.com/bokeh/bokeh/assets/99359668/237fded2-c79b-4413-bcfd-1f4561c39973)

Here's the book's plot:

![image](https://github.com/bokeh/bokeh/assets/99359668/ce3b5726-56fe-4088-8303-6f08e2a7e972)

As you can see, there is a visible difference in how the color bars are rendered.

user: Azaya89
issue_13244: state: open
title: [BUG] Rendering using BooleanFilter depends on chosen layout and if glyph shares view with DataTable
body: ### Software versions

Python version      :  3.8.2 (tags/v3.8.2:7b3ab59, Feb 25 2020, 23:03:10) [MSC v.1916 64 bit (AMD64)]
IPython version     :  7.21.0
Tornado version     :  6.1
Bokeh version       :  3.1.1
BokehJS static path :  C:\Users\dku\.extools\extractDir\python\python-3.8.2-win64-B64133\python-3.8.2.amd64\lib\site-packages\bokeh\server\static
node.js version     :  (not installed)
npm version         :  (not installed)
Operating system    :  Windows-10-10.0.19041-SP0

### Browser name and version

Firefox 110.0.1 (64-Bit)

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

I am trying to filter simultaneously a table and a plot using a BooleanFilter.

### Observed behavior

If it works or not depends on chosen layout and whether the glyph shares a view with the DataTable or not.

Working:
* shared view + layout Column(Row(filter_widget, plot), data_table)
* different sources or no view for glyph + any layout

Not working:
* shared view + layout Row(Column(filter_widget, data_table), plot)

This can be reproduced for Bokeh version 3.2 as well, see [Bokeh Discourse](https://discourse.bokeh.org/t/rendering-using-booleanfilter-depends-on-chosen-layout-and-if-glyph-shares-view-with-datatable/10625/1).

### Example code

```Python
import pandas as pd
from bokeh.plotting import figure, show
from bokeh.models import CustomJS, ColumnDataSource, CDSView, DataTable, TableColumn, CheckboxButtonGroup, Column, Row
from bokeh.models.filters import BooleanFilter

# ADAPT
# renders: use_working_layout & (use_view_for_plot True or False)
# not rendering: use_working_layout False & use_view_for_plot True
# renders: use_working_layout False & use_view_for_plot False
use_working_layout = True
use_view_for_plot = True

# create source
data = [
    {'a': 10, 'xs': [1, 2, 3, 4, 5], 'ys': [5, 7, 2, 7, 10], 'x': 1, 'y': 10},
    {'a': 100, 'xs': [1, 2, 3, 4, 5], 'ys': [2, 4, 3, 7, 6], 'x': 2, 'y': 11},
    {'a': 500, 'xs': [1, 2, 3, 4, 5], 'ys': [0.1, 0.7, 2, 2, 0.5], 'x': 3, 'y': 12},
    {'a': 100, 'xs': [1, 2, 3, 4, 5], 'ys': [1, 4, 5, 6, 2], 'x': 4, 'y': 13},
    {'a': 10, 'xs': [1, 2, 3, 4, 5], 'ys': [8, 6, 9, 3, 2], 'x': 5, 'y': 14}
]
df = pd.DataFrame(data=data)

col_to_filter = 'a'
df[col_to_filter] = df[col_to_filter].astype(str)

source = ColumnDataSource(df)

# view
boolean_filter = BooleanFilter(booleans=[True] * df.shape[0])
view = CDSView(filter=boolean_filter)

# create table
columns = []
for col in list(df.columns):
    columns.append(TableColumn(field=col, title=col, sortable=True))
data_table = DataTable(source=source, columns=columns, view=view)

# create plot
tooltips = [
    ("index", "$index"),  # shows '#' in data table
    ("(x,y)", "($x, $y)")
]
tools = "tap, pan, box_zoom, wheel_zoom, save, reset"
plot = figure(tooltips=tooltips, tools=tools)
multi_line = plot.multi_line(xs='xs', ys='ys', source=source)
if use_view_for_plot:
    multi_line.view = view

# filter widget
values = sorted(set(df[col_to_filter]))
values = [str(item) for item in values]
filter_widget = CheckboxButtonGroup(labels=values, active=list(range(len(values))))

filter_widget.js_on_change(
    "active", CustomJS(args=dict(source=source, filter=boolean_filter, col_filter=col_to_filter),
                       code="""
    const values = cb_obj.active.map(idx => cb_obj.labels[idx]);
    console.log('values:', values);
    filter.booleans = source.data[col_filter].map(v => values.includes(v));
    console.log('boolean filter:', filter.booleans);

    source.change.emit();
"""))

# create layout
if use_working_layout:
    layout = Column(Row(filter_widget, plot), data_table)  # works!
else:
    layout = Row(Column(filter_widget, data_table), plot)  # error!

show(layout)
```


### Stack traceback or browser console output

If it is not rendering, I get the error in Firefox console:
“Uncaught (in promise) TypeError: this.data is undefined”.

### Screenshots

_No response_
user: DanielaK123
issue_13241: state: open
title: [BUG] gridplot symmetry issues in bokeh 3.3.0
body: ### Software versions

Python version      :  3.10.0 | packaged by conda-forge | (default, Nov 20 2021, 02:18:13) [MSC v.1916 64 bit (AMD64)]
IPython version     :  (not installed)
Tornado version     :  6.3.2
Bokeh version       :  3.2.0
BokehJS static path :  C:\Users\***\anaconda3\envs\bokeh3\lib\site-packages\bokeh\server\static
node.js version     :  (not installed)
npm version         :  (not installed)
Operating system    :  Windows-10-10.0.22000-SP0

### Browser name and version

Chrome

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

In bokeh 2.* I am able to create symmetrical gridplots, even when elements of that grid are a column of other elements.
Consider the bottom-left element, which could be a column of [plot, plot]:
<img width="839" alt="image" src="https://github.com/bokeh/bokeh/assets/8526498/394da946-1f1a-4d96-af53-f0a7152c51a7">
or [plot, select]:
<img width="839" alt="image" src="https://github.com/bokeh/bokeh/assets/8526498/17aea6d5-ac88-486b-89e1-d84f66eb3018">

I expect to be able to create those plots in bokeh 3.2.0.

### Observed behavior

Starting from bokeh 3.0, those plots have been broken and they are not fixed by 3.2.0.

[plot, plot]:

<img width="842" alt="image" src="https://github.com/bokeh/bokeh/assets/8526498/83994432-4597-4803-a1d2-1108939591b9">

[plot, select]:

<img width="844" alt="image" src="https://github.com/bokeh/bokeh/assets/8526498/ddc697a1-6e69-4762-9839-7f89a68b17eb">


### Example code

```Python
"""Test plotting a grid in bokeh.

The 3rd (bottom left) element in the grid is a column of two sub-elements.

In bokeh 2.* this works as intended.

In bokeh 3.2.0, stretching is not symmetrical.

This happens if the two sub-elements are e.g.:
    - plot and dropdown
    - plot and another plot
"""
import numpy as np
import bokeh
from bokeh.layouts import gridplot, column
from bokeh.models import ColumnDataSource
from bokeh.models.widgets import Select
from bokeh.plotting import figure
from bokeh.io import curdoc, show

x = np.random.random(size=2000) * 1000
y = np.random.normal(size=2000) * 2 + 5
source = ColumnDataSource(dict(x=x, y=y))

def make_plot(title):
    p = figure(title=f"Element {title} (bokeh {bokeh.__version__})")
    p.scatter('x', 'y', alpha=0.8, source=source)
    p.yaxis.axis_label = 'y-axis Label [unit]'
    return p

p1 = make_plot("1")
p2 = make_plot("2")
p3 = make_plot("3.1")
p4 = make_plot("3.2")
p5 = make_plot("4")

dropdown_graph = Select(
    title="Some other dropdown",
    value='Test', options=['Test'],
    # width=50,
    # height=50,
    # sizing_mode='fixed',  # Even setting a fixed mode does not help
    sizing_mode='stretch_width',  # Works in bokeh 2.4
    )

grid = gridplot([
    [p1, p2],
    # Option a): Works fine in Bokeh 3.2.0
    # [p3, p5],
    # Option b): Dropdown stretches too wide in Bokeh 3.2.0
    # [column([p3, dropdown_graph], sizing_mode='stretch_both'), p4],
    # Option c): Two plots are also broken in Bokeh 3.2.0
    [column([p3, p4], sizing_mode='stretch_both'), p5],
    ],
    sizing_mode='stretch_both',
    )

curdoc().add_root(grid)
curdoc().title = "Grid Test"

show(grid)
```


### Stack traceback or browser console output

The browser console does not report any issues.

### Screenshots

See above


This may or may not be related to #13218. It is really hard to tell with these layout issues.
user: jnettels
issue_13240: state: open
title: [BUG] Can't change text_font on LabelSet/HTMLLabelSet
body: ### Software versions

Python version      :  3.10.10 (main, Apr  2 2023, 07:50:25) [GCC 11.3.1 20221209]
IPython version     :  (not installed)
Tornado version     :  6.3.1
Bokeh version       :  3.2.0
BokehJS static path :  /home/godlike/virtualenv/recession/lib/python3.10/site-packages/bokeh/server/static
node.js version     :  v18.14.2
npm version         :  9.5.0
Operating system    :  Linux-5.15.102-gentoo-x86_64-AMD_Ryzen_7_2700_Eight-Core_Processor-with-glibc2.36


### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

It should be possible to change the font of text that belongs to a LabelSet / HTMLLabelSet by specifying text_font when creating the labelset.

### Observed behavior

The font is always helvetica (the default). I can even check the created labelset's attributes and the attributes inside p.center[x] where 'x' is the index of the labelset. Both show the value I specified when creating the labelset, yet when displayed the font is clearly not sans-serif (it can be seen most evident on the '1' and the '5'). Note that I can change text_color and text_font_size without any problems, but the text_font never changes.

### Example code

```Python
def get_test_graph():
    x_values, y_values = [[1, 2, 3, 4], [1, 2, 3, 4]]
    source = ColumnDataSource(
        data=dict(columns=x_values, counts=y_values, color=Bright6)
    )
    p = figure(
        x_range=(0, max(x_values) * 1.30),
        y_range=(0, max(y_values) * 1.30),
        height=350,
        width=500,
        title=f"Test graph",
        toolbar_location=None,
        tools="hover",
        tooltips="@columns: @counts",
    )
    p.vbar(
        x="columns",
        top="counts",
        width=0.5,
        color="color",
        legend_field="columns",
        source=source,
    )
    labels = LabelSet(
        x="columns",
        y="counts",
        text="counts",
        x_offset=0,
        y_offset=5,
        source=source,
        text_font="sans-serif",
        text_color="red",
        text_font_size="24px",
        text_align="center",
    )
    p.add_layout(labels)
    p.xgrid.grid_line_color = None
    p.legend.orientation = "horizontal"
    p.legend.location = "top_center"
    p.legend.label_text_font = "sans-serif"
    p.legend.title_text_font = "sans-serif"
    p.title.text_font = "sans-serif"
    p.xaxis.axis_label_text_font = "sans-serif"
    p.yaxis.axis_label_text_font = "sans-serif"
    p.xaxis.major_label_text_font = "sans-serif"
    p.yaxis.major_label_text_font = "sans-serif"
    if max(y_values) < 10:
        p.yaxis[0].ticker = list(range(0, max(y_values) * 2))
    script, div = components(p)
    return (script, div)
```


### Stack traceback or browser console output

_No response_

### Screenshots

![Screenshot_20230702_091819](https://github.com/bokeh/bokeh/assets/182708/b6adb350-0985-4f92-b1a7-6ba49e983c4f)

user: godlike64
issue_13237: state: open
title: [BUG] LaTeX axis label broken when exported as SVG
body: ### Software versions

Python version      :  3.10.12 | packaged by conda-forge | (main, Jun 23 2023, 22:34:57) [MSC v.1936 64 bit (AMD64)]
IPython version     :  (not installed)lled
Tornado version     :  6.3.2
Bokeh version       :  3.2.0
BokehJS static path :  C:\Users\savchenko\.conda\envs\svg\lib\site-packages\bokeh\server\static
node.js version     :  (not installed)
npm version         :  (not installed)
Operating system    :  Windows-10-10.0.19044-SP0

### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

The image with LaTeX axis label exported as SVG file should look like it does in jupyter/html. And even if the label is rasterized, it should at least be of the comparable quality to the rest of the figure.

### Observed behavior

The produced SVG looks good when viewed from a browser:
![image](https://github.com/bokeh/bokeh/assets/22128555/d312970e-f333-4d67-b24f-c92312e3a11a)

However, the following weird behaviors are observed:
- when adding the image to a PDF in LaTeX (using the `svg` package and `\includesvg` command), the axis label is not shown at all
- when opening the svg with inkscape, the label is also not shown at all:
![image](https://github.com/bokeh/bokeh/assets/22128555/bc9d25e0-f345-4741-b07d-1bcf104a466e)
- when opening the svg with GIMP the label seems to be rasterized with an incredibly low resolution compared to the rest of the image:
![image](https://github.com/bokeh/bokeh/assets/22128555/7ff7c72e-a60e-410a-b295-c8eb2fdad61f)
![GIMP_Export_LaTeX_Label](https://github.com/bokeh/bokeh/assets/22128555/bd045eb4-4c61-43c9-a686-6fa1cddc55f6)
here's the comparison of the same steps (export_svgs -> open in GIMP) if we use plain text label instead:
![GIMP_Export_plain_label](https://github.com/bokeh/bokeh/assets/22128555/861420a0-b22e-4cfa-a289-70659297b83b)

This was tested with a fresh environment:
```
conda env export --from-history
name: svg
channels:
  - conda-forge
  - defaults
dependencies:
  - python=3.10
  - bokeh
  - selenium
  - ca-certificates
  - openssl
  - firefox
  - geckodriver
  - certifi
```


### Example code

```Python
import bokeh,numpy
from bokeh.plotting import figure
from bokeh.io import export_svgs
print(bokeh.__version__)
p=figure(width=400,height=200)
p.line(numpy.linspace(0,10,100),numpy.sin(numpy.linspace(0,10,100)))
p.yaxis.axis_label = r'\[x_{\mathrm{cart}}\]'
# the non-latex axis label is handled correctly - it appears in inkscape and isn't blurry in GIMP
# p.yaxis.axis_label = 'x_cart' 
p.yaxis.axis_label_text_font_size = "36pt"
p.output_backend='svg'
export_svgs(p,filename='min_example.svg')
```


### Stack traceback or browser console output

_No response_

### Screenshots

_No response_
user: AntonSavchenko
issue_13233: state: open
title: [BUG] Hickup when changing visibilities despite using curdoc::hold
body: ### Software versions

Python version      :  3.9.16 (main, Mar  8 2023, 10:39:24) [MSC v.1916 64 bit (AMD64)]
IPython version     :  8.12.0
Tornado version     :  6.2
Bokeh version       :  3.2.0
BokehJS static path :  C:\Users\<my username>\anaconda3\lib\site-packages\bokeh\server\static
node.js version     :  (not installed)
npm version         :  (not installed)
Operating system    :  Windows-10-10.0.22621-SP0

### Browser name and version

Firefox 114.0.2 (64-bit)

### Expected behavior

In the example code below, the goal is to have plot which can show a "loading screen" while data is processed (after hitting the Create button). In the example, random points are generated, but in a real life application, this would be especially for reading and processing data which can take notably long (over a second).
For that, a plot which serves as a loading screen (empty plot except for a label that says "Loading") already exists as an object, is positioned at another place in a layout, and in order to switch from seeing the actual plot and the loading screen, visibility is changed.
In order to have a smooth transition, each change of visibility is started by calling `curdoc().hold("combine")`, and ended with `unhold`.

### Observed behavior

A smooth transition does not happen. For a split (but distracting) moment, we see both plots, and the button "hickups" downwards.
If we change the order of the visibility assignments, we see no plot at all for a split moment, with the button hickup going upwards.
Something like assigning both in the same assignment as tuple, hoping that it would behave somewhat atomic, doesn't work either.

In any case, I had expected that `curdoc::hold` would switch the plots at once anyway.


### Example code

```Python
from bokeh.plotting import figure, curdoc
from bokeh.models import ColumnDataSource, Label, Button
from bokeh.layouts import column
from random import *

def loading_pic(width: int = 500, height: int = 500) -> figure:
    plot = figure(x_range=(0,width), y_range=(0,height), width=width, height=height, tools='')
    pos_x = width/2 
    pos_y = height/2    
    
    label_size = min(height/4, width/4)
    label_size_str = str(label_size)+'px'
    
    loading_label = Label(x=pos_x, y=pos_y, text='Loading', text_font_size = label_size_str,
                          text_align='center', text_baseline='middle')
    plot.add_layout(loading_label)
    
    plot.xaxis.visible = False
    plot.yaxis.visible = False
    plot.xgrid.grid_line_color = None
    plot.ygrid.grid_line_color = None
    
    return plot
    
def random_coords(maximum: float = 500, amount: int = 10000) -> ColumnDataSource:
    data = ColumnDataSource()
    data.data['x'] = [random()*maximum for i in range(amount)]
    data.data['y'] = [random()*maximum for i in range(amount)]
    return data
    
class MyPlot:
    
    def __init__(self, data: ColumnDataSource):
        self.plot = figure(x_range=(0,500), y_range=(0,500), width=500, height=500)
        self.plot.circle(source=data, x='x', y='y')
    
class MyFrame:

    def __init__(self):

        self._loading = loading_pic()

        self._loading_container = column(self._loading)
        self._render_container = column(column())
        self._render_container.visible = False
        
        self.layout = column(self._loading_container,  self._render_container)

    def display_loading(self) -> None:
        curdoc().hold("combine")
        self._loading_container.visible = True
        self._render_container.visible = False
        curdoc().unhold()

    def plot(self) -> None:
        curdoc().hold("combine")
        data = random_coords()
        self._my_plot = MyPlot(data)
        print("plot:", self._my_plot.plot)
        self._render_container.children[0] = self._my_plot.plot
        self._loading_container.visible = False
        self._render_container.visible = True
        curdoc().unhold()
        
    def plot_delayed(self, event) -> None:
        self.display_loading()
        curdoc().add_next_tick_callback(self.plot) 
        
my_frame = MyFrame()
        
button = Button(label='Create')
button.on_event('button_click', my_frame.plot_delayed)
       
layout = column(my_frame.layout, button)

curdoc().add_root(layout)

my_frame.plot_delayed(None)
```


### Additional Information

In order to trigger the behavior, simply click on the button.

I tested  `curdoc::hold` by commenting the `unhold` and it stays frozen as expected, meaning the function does work, in some way. Also, in my actual bigger project, I use it successfully to accumulate small changes to a single plot.

This issue was created after discussions in the Bokeh forums, see https://discourse.bokeh.org/t/bug-with-deserializationerror-when-changing-content-layout-in-bokeh-3-1-1/10566/11

Note that the code is almost the same as in a different issue I posted recently.
user: Aziuth
issue_13231: state: open
title: [FEATURE] RadioButtonGroup does not support Latex.
body: ### Software versions

Python 3.10
Bokeh 3.2
Win 10

### Browser name and version

Firefox

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

Proper latex rendering.

### Observed behavior

![image](https://github.com/bokeh/bokeh/assets/60094563/ce4117b5-82b9-455c-b4a5-29cf9a7bc2cc)

### Example code

```Python
from bokeh.io import show, output_notebook
from bokeh.models import CustomJS, RadioButtonGroup

#output_notebook()

lab = r"$$\text{Dependency on }R_{core}$$"
rbg = RadioButtonGroup(labels=['Dependency on N', lab],active=1)

show(rbg)
```


### Stack traceback or browser console output

_No response_

### Screenshots

_No response_
user: byquip
labels: tag: MathText: Related to MathText (TeX/MathML/AsciiMath)
issue_13230: state: open
title: [FEATURE] Event x-y data coordinates sometimes need to be converted to categorical values
body: ### Problem description

In Bokeh using python events, I am able to catch Tap events in python code using a callback:
```
def tap_callback(event):
        print(f"model={event.model} x={event.x} "
                f"y={event.y} sx={event.sx} sy={event.sy}")
```

The issue occurs when one of the axes is *categorical*. In my case it is the y-axis, but it could be the x-axis as well. In this case, we wish to translate the `event.y` *data* coordinate to a categorical *name*. In a specific case, I succeeded in doing it like this:
```
from math import floor
def tap_callback(event):
    print(f"y={event.y} feature={event.model.y_range.factors[floor(event.y)]}")
```
This works very well for my simple case. The more general request would be for some function such as:
```
def tap_callback(event):
        name_tapped = event.model.get_name_from_coordinate(event.y)
        print(name_tapped)
```
This is needed because, as @bryevdv mentions (see conversation link below), if "you were using nested categories or some of the more obscure categorical axis settings, then this simple transformation would probably not work as-is".

Please also see the conversation with @bryevdv at [event x-y coordinates need to become categorical coordinates](https://discourse.bokeh.org/t/tap-event-x-y-coordinates-need-to-become-categorical-coordinates/10589)

### Feature description

When categorical ranges are used, a method call available on the model objects that translates a data coordinate to the name of the corresponding category. In particular this method call needs to be available inside an event callback. For example:

```
def tap_callback(event):
        name_tapped = event.model.get_name_from_coordinate(event.y)
        print(name_tapped)
```


### Potential alternatives

In a simple case the following works
```
from math import floor
def tap_callback(event):
    print(f"y={event.y} feature={event.model.y_range.factors[floor(event.y)]}")
```
but not if "you were using nested categories or some of the more obscure categorical axis settings, then this simple transformation would probably not work as-is"

### Additional information

Please also see the conversation with @bryevdv at [event x-y coordinates need to become categorical coordinates](https://discourse.bokeh.org/t/tap-event-x-y-coordinates-need-to-become-categorical-coordinates/10589)
user: giladpn
issue_13229: state: open
title: [BUG] DeserializationError when changing the content of a layout
body: ### Software versions

Python version      :  3.9.16 (main, Mar  8 2023, 10:39:24) [MSC v.1916 64 bit (AMD64)]
IPython version     :  8.12.0
Tornado version     :  6.2
Bokeh version       :  3.1.1
BokehJS static path :  C:\Users\<my username>\anaconda3\lib\site-packages\bokeh\server\static
node.js version     :  (not installed)
npm version         :  (not installed)
Operating system    :  Windows-10-10.0.22621-SP0

### Browser name and version

Firefox 114.0.2 (64-bit)

### Expected behavior

Within a Bokeh server application, I have a layout in which I render a plot which is to be exchanged when a button is pressed. In between, a loading screen (a plot with just a label that says "Loading") is to be displayed.

### Observed behavior

It works, but each time the exchange is made, it produces an error like
`bokeh.core.serialization.DeserializationError: can't resolve reference 'p1074'`
with the reference being the last plot.

### Example code

```Python
from bokeh.plotting import figure, curdoc
from bokeh.models import ColumnDataSource, Label, Button
from bokeh.layouts import column
from random import *

def loading_pic(width: int = 500, height: int = 500) -> figure:
    plot = figure(x_range=(0,width), y_range=(0,height), width=width, height=height, tools='')
    pos_x = width/2 
    pos_y = height/2    
    
    label_size = min(height/4, width/4)
    label_size_str = str(label_size)+'px'
    
    loading_label = Label(x=pos_x, y=pos_y, text='Loading', text_font_size = label_size_str,
                          text_align='center', text_baseline='middle')
    plot.add_layout(loading_label)
    
    plot.xaxis.visible = False
    plot.yaxis.visible = False
    plot.xgrid.grid_line_color = None
    plot.ygrid.grid_line_color = None
    
    return plot
    
def random_coords(maximum: float = 500, amount: int = 10000) -> ColumnDataSource:
    data = ColumnDataSource()
    data.data['x'] = [random()*maximum for i in range(amount)]
    data.data['y'] = [random()*maximum for i in range(amount)]
    return data
    
class MyPlot:
    
    def __init__(self, data: ColumnDataSource):
        self.plot = figure(x_range=(0,500), y_range=(0,500), width=500, height=500)
        self.plot.circle(source=data, x='x', y='y')
    
class MyFrame:

    def __init__(self):

        self._loading = loading_pic()

        self._loading_container = column(self._loading)
        self._render_container = column(column())
        self._render_container.visible = False
        
        self.layout = column(self._loading_container,  self._render_container)

    def display_loading(self) -> None:
        curdoc().hold("combine")
        self._loading_container.visible = True
        self._render_container.visible = False
        curdoc().unhold()

    def plot(self) -> None:
        curdoc().hold("combine")
        data = random_coords()
        self._my_plot = MyPlot(data)
        print("plot:", self._my_plot.plot)
        self._render_container.children[0] = self._my_plot.plot
        self._loading_container.visible = False
        self._render_container.visible = True
        curdoc().unhold()
        
    def plot_delayed(self, event) -> None:
        self.display_loading()
        curdoc().add_next_tick_callback(self.plot) 
        
my_frame = MyFrame()
        
button = Button(label='Create')
button.on_event('button_click', my_frame.plot_delayed)
       
layout = column(my_frame.layout, button)

curdoc().add_root(layout)
```

### Additional Info

To trigger the bug, the "Create" button has to be hit *twice*. The first click works fine, but each click afterwards produces the error.

In order to make my code example quicker to read, I tried to reduce it further, especially by removing the loading screen. The bug stops to exist at some point. The quickest way to make it disappear is by removing the lines which make the "container" objects visible or invisible.

I already posted the issue in the Bokeh forums (initially in a different form) and we had some discussion regarding it:
https://discourse.bokeh.org/t/bug-with-deserializationerror-when-changing-content-layout-in-bokeh-3-1-1/10566/8
[And I also have another post on SO: [https://stackoverflow.com/questions/76506289/bokeh-3-1-1-deserializationerror-when-changing-layout-content](https://stackoverflow.com/questions/76506289/bokeh-3-1-1-deserializationerror-when-changing-layout-content) ]

Note that while in this reduced example, I could easily fix the code by replacing the source of the plot instead of the whole plot at all, I dislike this strategy in the big picture. The actual plots I am using have a lot of elements, and changing their sources seems error-prone to me, possible carrying over artifacts from old plots.

Also, my actual code (which was the base for the reduced code) worked without this error under Bokeh 2.4.3, and it occured only after updating to Bokeh 3.1.1.

### Stack traceback in Anaconda Powershell

```
2023-06-23 11:44:24,383 error handling message
 message: Message 'PATCH-DOC' content: {'events': [{'kind': 'ModelChanged', 'model': {'id': 'p1074'}, 'attr': 'inner_width', 'new': 0}, {'kind': 'ModelChanged', 'model': {'id': 'p1074'}, 'attr': 'inner_height', 'new': 0}, {'kind': 'ModelChanged', 'model': {'id': 'p1074'}, 'attr': 'outer_width', 'new': 0}, {'kind': 'ModelChanged', 'model': {'id': 'p1074'}, 'attr': 'outer_height', 'new': 0}]}
 error: DeserializationError("can't resolve reference 'p1074'")
Traceback (most recent call last):
  File "C:\Users\<my username>\anaconda3\lib\site-packages\bokeh\server\protocol_handler.py", line 97, in handle
    work = await handler(message, connection)
  File "C:\Users\<my username>\anaconda3\lib\site-packages\bokeh\server\session.py", line 94, in _needs_document_lock_wrapper
    result = func(self, *args, **kwargs)
  File "C:\Users\<my username>\anaconda3\lib\site-packages\bokeh\server\session.py", line 288, in _handle_patch
    message.apply_to_document(self.document, self)
  File "C:\Users\<my username>\anaconda3\lib\site-packages\bokeh\protocol\messages\patch_doc.py", line 104, in apply_to_document
    invoke_with_curdoc(doc, lambda: doc.apply_json_patch(self.payload, setter=setter))
  File "C:\Users\<my username>\anaconda3\lib\site-packages\bokeh\document\callbacks.py", line 443, in invoke_with_curdoc
    return f()
  File "C:\Users\<my username>\anaconda3\lib\site-packages\bokeh\protocol\messages\patch_doc.py", line 104, in <lambda>
    invoke_with_curdoc(doc, lambda: doc.apply_json_patch(self.payload, setter=setter))
  File "C:\Users\<my username>\anaconda3\lib\site-packages\bokeh\document\document.py", line 369, in apply_json_patch
    patch: PatchJson = deserializer.deserialize(patch_json)
  File "C:\Users\<my username>\anaconda3\lib\site-packages\bokeh\core\serialization.py", line 501, in deserialize
    return self.decode(obj.content, obj.buffers)
  File "C:\Users\<my username>\anaconda3\lib\site-packages\bokeh\core\serialization.py", line 516, in decode
    return self._decode(obj)
  File "C:\Users\<my username>\anaconda3\lib\site-packages\bokeh\core\serialization.py", line 557, in _decode
    return {key: self._decode(val) for key, val in obj.items()}
  File "C:\Users\<my username>\anaconda3\lib\site-packages\bokeh\core\serialization.py", line 557, in <dictcomp>
    return {key: self._decode(val) for key, val in obj.items()}
  File "C:\Users\<my username>\anaconda3\lib\site-packages\bokeh\core\serialization.py", line 559, in _decode
    return [self._decode(entry) for entry in obj]
  File "C:\Users\<my username>\anaconda3\lib\site-packages\bokeh\core\serialization.py", line 559, in <listcomp>
    return [self._decode(entry) for entry in obj]
  File "C:\Users\<my username>\anaconda3\lib\site-packages\bokeh\core\serialization.py", line 557, in _decode
    return {key: self._decode(val) for key, val in obj.items()}
  File "C:\Users\<my username>\anaconda3\lib\site-packages\bokeh\core\serialization.py", line 557, in <dictcomp>
    return {key: self._decode(val) for key, val in obj.items()}
  File "C:\Users\<my username>\anaconda3\lib\site-packages\bokeh\core\serialization.py", line 555, in _decode
    return self._decode_ref(cast(Ref, obj))
  File "C:\Users\<my username>\anaconda3\lib\site-packages\bokeh\core\serialization.py", line 569, in _decode_ref
    self.error(f"can't resolve reference '{id}'")
  File "C:\Users\<my username>\anaconda3\lib\site-packages\bokeh\core\serialization.py", line 717, in error
    raise DeserializationError(message)
bokeh.core.serialization.DeserializationError: can't resolve reference 'p1074'
```
user: Aziuth
issue_13225: state: open
title: [BUG] Gridplot with one figure having a title and another `frame_height` removes xlabel
body: ### Software versions

``` yaml
Python version      :  3.11.4 | packaged by conda-forge | (main, Jun 10 2023, 18:08:17) [GCC 12.2.0]
IPython version     :  8.14.0
Tornado version     :  6.3.2
Bokeh version       :  3.2.0.dev4+15.g582a182d.dirty
BokehJS static path :  /home/shh/Repos/bokeh/src/bokeh/server/static
node.js version     :  v18.15.0
npm version         :  9.7.1
Operating system    :  Linux-6.2.6-76060206-generic-x86_64-with-glibc2.35
```

### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

No xlabel should disappear. 

### Observed behavior

Using a `gridplot` where one has a `title` set and the other one has a `frame_height` set. It will remove the second figures xlabel.  

First reported here: https://github.com/holoviz/holoviews/issues/5771

### Example code

```Python
from bokeh.plotting import figure, gridplot, show

p1 = figure(title="title", x_axis_label="x", y_axis_label="y")
p1.line(x=[0, 1], y=[0, 1])

p2 = figure(x_axis_label="x", y_axis_label="y", frame_height=200)
p2.line(x=[0, 1], y=[0, 1])

show(gridplot(children=[[p1, p2]]))
```


### Stack traceback or browser console output

_No response_

### Screenshots

![image](https://github.com/bokeh/bokeh/assets/19758978/c375ab4b-2805-44c7-8ee3-a1f5e50e16b9)

user: Hoxbro
issue_13218: state: open
title: gridplot layout broken in splom example
body: ref: https://docs.bokeh.org/en/dev-3.2/docs/examples/topics/stats/splom.html

<img width="970" alt="Screenshot 2023-06-18 at 19 43 10" src="https://github.com/bokeh/bokeh/assets/1078448/89f207f8-d6e5-426d-a237-bc08b3eab92d">

cc @bokeh/core I would suggest delay for this one as well. 
user: bryevdv
issue_13216: state: open
title: Add support for glyph decorations and decoration patterns to all glyphs
body: So far glyph decorations were added on an experimental basis to `Segment` and `Arc` glyphs, as a proof of concept. This needs to be extended to other glyphs where it makes sense (e.g. `Line`, `MultiLine`, `Bezier`). Additionally glyph decoration patterns should be added to such glyphs and other glyphs (e.g. `Circle`, `Rect`), though this may deserve its own tracking issue.
user: mattpap
labels: grant: SDG 2023: Funded from NumFOCUS Small Development Grant 2023
issue_13206: state: open
title: Input validation for ImageStack glyph
body: I would like to add input validation for `ImageStack` glyph on the Python side. This already occurs on the JavaScript side, but as pointed out by @Hoxbro errors on the browser side are not as user-friendly as those on the Python side. I have had a look at implementing this and discovered that what I would like to do is currently not possible using the existing `@error` decorator, although @mattpap has some ideas on improvements in this area. Hence I am writing this issue to document the checks I would like to make so that they are not forgotten.

An `ImageStack` glyph has a `color_mapper` that is a `WeightedStackColorMapper` and a scalar or vector `image` that is/are multidimensional arrays (think `numpy` arrays). Eventually more options will be supported. These are the checks I would like to make (in Python/pseudocode):
```python
# self is an ImageStack here
assert isinstance(self.color_mapper, WeightedStackColorMapper)
n_colors = len(self.color_mapper.palette)
assert n_colors > 0

for image in self.image:
    assert image.ndim == 3
    assert image.shape[0] > 0
    assert image.shape[1] > 0
    assert image.shape[2] == n_colors
```
This isn't currently possible as by the time the `@error` validator is called, whatever data that was passed in as the `image` has been placed within a `ColumnDataSource` and this CDS is not visible in the validator. `self.image` contains the string name of the image field in the CDS.

Probably we would also add validators for the other `ImageBase`-derived models which have some expectations about the number of dimensions, etc.
user: ianthomas23
issue_13205: state: open
title: [BUG] Custom theme is not applied to html output unsing `output_file()` and `show()`
body: ### Expected behavior

I expect that it is possible to show and save a figure as html with custom theme in one step.

### Observed behavior

If I try to use a custom theme in a jupyter notebook and display the figure unsing `show()` and try to save the file as html file at the same time, the custom theme is only applied once.

```Python
from bokeh.plotting import curdoc, figure, output_file, output_notebook, save, show
output_notebook()

x = [1, 2, 3, 4, 5]
y = [6, 7, 6, 4, 5]

curdoc().theme = 'dark_minimal'

p = figure(title='dark_minimal', width=300, height=300)
p.line(x, y)

output_file(f"test.html", mode="inline")
# save(p, filename=f"test.html", resources=None)
show(p)
```

|output in notebook|output in saved "test.html"|
|--|--|
|![grafik](https://github.com/bokeh/bokeh/assets/68053396/74e49e96-6da2-44a2-98c6-073c7a5582c3)|![grafik](https://github.com/bokeh/bokeh/assets/68053396/04fa589a-7718-4f87-9474-bc9dc0f856a2)|

The alternative approach first saving the html file using `save()` creates a file with correct theme but it can not be displayed in the notebook afterwards. The function `show()` raises a runtime error
`RuntimeError: Models must be owned by only a single document, GlyphRenderer(id='p1372', ...) is already in a doc`.

```python
from bokeh.plotting import curdoc, figure, output_file, output_notebook, save, show
output_notebook()

x = [1, 2, 3, 4, 5]
y = [6, 7, 6, 4, 5]

curdoc().theme = 'dark_minimal'

p = figure(title='dark_minimal', width=300, height=300)
p.line(x, y)

output_file(f"test.html", mode="inline")
save(p, filename=f"test.html", resources=None)
show(p)
```
|"test.html" created by `save()`|
|--|
|![grafik](https://github.com/bokeh/bokeh/assets/68053396/3c4916ad-e863-479a-8a37-46ee7a6ca667)|

<details><summary>Traceback</summary>

---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
Input In [3], in <cell line: 14>()
     12 output_file(f"test.html", mode="inline")
     13 save(p, filename=f"test.html", resources=None)
---> 14 show(p)

File /opt/conda/envs/aiva-env/lib/python3.9/site-packages/bokeh/io/showing.py:143, in show(obj, browser, new, notebook_handle, notebook_url, **kwargs)
    140 state = curstate()
    142 if isinstance(obj, UIElement):
--> 143     return _show_with_state(obj, state, browser, new, notebook_handle=notebook_handle)
    145 def is_application(obj: Any) -> TypeGuard[Application]:
    146     return getattr(obj, '_is_a_bokeh_application_class', False)

File /opt/conda/envs/aiva-env/lib/python3.9/site-packages/bokeh/io/showing.py:190, in _show_with_state(obj, state, browser, new, notebook_handle)
    188 if state.notebook:
    189     assert state.notebook_type is not None
--> 190     comms_handle = run_notebook_hook(state.notebook_type, 'doc', obj, state, notebook_handle)
    191     shown = True
    193 if state.file or not shown:

File /opt/conda/envs/aiva-env/lib/python3.9/site-packages/bokeh/io/notebook.py:367, in run_notebook_hook(notebook_type, action, *args, **kwargs)
    365 if _HOOKS[notebook_type][action] is None:
    366     raise RuntimeError(f"notebook hook for {notebook_type!r} did not install {action!r} action")
--> 367 return _HOOKS[notebook_type][action](*args, **kwargs)

File /opt/conda/envs/aiva-env/lib/python3.9/site-packages/bokeh/io/notebook.py:583, in show_doc(obj, state, notebook_handle)
    579 '''
    580 
    581 '''
    582 if obj not in state.document.roots:
--> 583     state.document.add_root(obj)
    585 from ..embed.notebook import notebook_content
    586 comms_target = make_id() if notebook_handle else None

File /opt/conda/envs/aiva-env/lib/python3.9/site-packages/bokeh/document/document.py:319, in Document.add_root(self, model, setter)
    316     return
    318 with self.models.freeze():
--> 319     self._roots.append(model)
    321 self.callbacks.trigger_on_change(RootAddedEvent(self, model, setter))

File /opt/conda/envs/aiva-env/lib/python3.9/contextlib.py:126, in _GeneratorContextManager.__exit__(self, typ, value, traceback)
    124 if typ is None:
    125     try:
--> 126         next(self.gen)
    127     except StopIteration:
    128         return False

File /opt/conda/envs/aiva-env/lib/python3.9/site-packages/bokeh/document/models.py:135, in DocumentModelManager.freeze(self)
    133 self._push_freeze()
    134 yield
--> 135 self._pop_freeze()

File /opt/conda/envs/aiva-env/lib/python3.9/site-packages/bokeh/document/models.py:288, in DocumentModelManager._pop_freeze(self)
    286 self._freeze_count -= 1
    287 if self._freeze_count == 0:
--> 288     self.recompute()

File /opt/conda/envs/aiva-env/lib/python3.9/site-packages/bokeh/document/models.py:235, in DocumentModelManager.recompute(self)
    232     md._detach_document()
    234 for ma in to_attach:
--> 235     ma._attach_document(document)
    236     self._new_models.add(ma)
    238 self._models = recomputed

File /opt/conda/envs/aiva-env/lib/python3.9/site-packages/bokeh/model/model.py:575, in Model._attach_document(self, doc)
    572     return
    574 if self.document is not None:
--> 575     raise RuntimeError(f"Models must be owned by only a single document, {self!r} is already in a doc")
    577 doc.theme.apply_to_model(self)
    578 self.document = doc

RuntimeError: Models must be owned by only a single document, GlyphRenderer(id='p1372', ...) is already in a doc

</details>

<details><summary>Software Information</summary>
### Software versions

Python version      :  3.9.16 | packaged by conda-forge | (main, Feb  1 2023, 21:39:03) 
IPython version     :  8.14.0
Tornado version     :  6.3.2
Bokeh version       :  3.1.1+1.g82b34cb3
BokehJS static path :  /home/jovyan/private/bokeh/src/bokeh/server/static
node.js version     :  v18.15.0
npm version         :  9.7.1
Operating system    :  Linux-4.19.0-21-amd64-x86_64-with-glibc2.31

### Browser name and version

Chrom Version 114.0.5735.110

### Jupyter notebook / Jupyter Lab version

Selected Jupyter core packages...
IPython          : 8.14.0
ipykernel        : 6.23.1
ipywidgets       : not installed
jupyter_client   : 8.2.0
jupyter_core     : 5.3.0
jupyter_server   : 2.6.0
jupyterlab       : not installed
nbclient         : 0.8.0
nbconvert        : 7.4.0
nbformat         : 5.9.0
notebook         : 6.5.4
qtconsole        : not installed
traitlets        : 5.9.0
</details>
user: mosc9575
issue_13191: state: open
title: Can't align stretching children in flex layouts
body: ```py
from bokeh.plotting import figure, show

p = figure(width=200, height=200, align="center")
p.circle([1, 2, 3], [1, 2, 3], size=20)

from bokeh.models import Column
c0 = Column(children=[p], align="center", max_width=300, sizing_mode="stretch_width", styles=dict(background_color="yellow"))
c1 = Column(children=[c0], sizing_mode="stretch_width", styles=dict(background_color="pink"))
show(c1)
```
![image](https://github.com/bokeh/bokeh/assets/27475/87241e8e-374c-490d-9e30-62dd765128a5)

`c0` (yellow) should be centered along the width of `c1` (pink), the same way as `p` is centered along the width of `c0`.

Originally reported in https://github.com/holoviz/panel/issues/5070. See also the explanation in https://github.com/holoviz/panel/issues/5070#issuecomment-1582570107.
user: mattpap
issue_13189: state: open
title: [BUG] Can't overwrite toolbar css using InlineStyleSheet
body: ### Software versions

Python version      :  3.9.10 | packaged by conda-forge | (main, Feb  1 2022, 21:24:11) 
IPython version     :  8.4.0
Tornado version     :  6.1
Bokeh version       :  3.1.1
BokehJS static path :  /opt/conda/envs/aiva-env/lib/python3.9/site-packages/bokeh/server/static
node.js version     :  v17.4.0
npm version         :  8.3.1
Operating system    :  Linux-4.19.0-21-amd64-x86_64-with-glibc2.31

### Browser name and version

Chrome Version 114.0.5735.110

### Expected behavior

I tried to overwrite the pre-defiend color for the icons in the toolbar (`bokeh-icon-color` and `bokeh-icon-color-disabled`) but my changes are not visible in the result.

The example in the docs [for styling](https://docs.bokeh.org/en/3.1.1/docs/user_guide/styling/dom.html) are working on a slider very well and at the moment I don't understand why the same approach does not work on the tollbar.

### Observed behavior

The changes are applied but not set to active.

![toolbar-css](https://github.com/bokeh/bokeh/assets/68053396/6300b870-4bad-4390-b3d4-b160932d178b)


### Example code

```Python
from bokeh.models import InlineStyleSheet
from bokeh.plotting import figure, show, output_notebook
output_notebook()

stylesheet = InlineStyleSheet(css="""
:host {
    --bokeh-icon-color: #666666;
    --bokeh-icon-color-disabled: #555555;
}
""")

p = figure(width=200, height=100, tools='pan')
p.line([1,2], [1,2])
p.toolbar.stylesheets = [stylesheet]
show(p)
```

|result|expected|
|--|--|
|![result_toolbar](https://github.com/bokeh/bokeh/assets/68053396/bb511a15-aa4a-4d0c-b9e7-563bf03a9433)|![expected_toolbar](https://github.com/bokeh/bokeh/assets/68053396/b57a7aba-0feb-42dd-ab6e-7a85c7123882)|

user: mosc9575
issue_13186: state: open
title: [FEATURE] datetime formatting: enhance default formatting
body: ### Problem description

Note: This probably needs some discussion.

Currently the default formatting provides ticks with mixed formats (see screenshots below). In my opinion this makes it extremely difficult to read the axis and to be honest I don't see any benefit over just having the same formatting for all ticks.
The first screenshot is probably an extreme example with three different formats but it highlights pretty well what the problem is.

![bokeh_formatter_dt_3](https://github.com/bokeh/bokeh/assets/71090451/5c5e07e8-1c1b-4bb0-b5de-3b21c0994303)
![bokeh_formatter_dt_2](https://github.com/bokeh/bokeh/assets/71090451/a1864e33-e972-4184-9914-49a5901da566)

Code for reference:

```
import pandas as pd
from bokeh.io import curdoc
from bokeh.models import DatetimeTickFormatter
from bokeh.plotting import figure

import numpy as np

t = pd.date_range("2023-06-01", periods=11).tz_localize("US/Pacific") #.tz_localize(None)
print(t)
y = np.linspace(0, 10, 11)

fig = figure(x_axis_type="datetime")
fig.line(t, y)

doc = curdoc()
doc.add_root(fig)
```

### Feature description

Improve default formatting by avoiding mixed formats. Perhaps it might also be possible to adapt a Matlab style formatting which provides an additional label for the whole axis.

![bokeh_formatter_dt_4](https://github.com/bokeh/bokeh/assets/71090451/101fc8a6-3c0b-433a-8056-98750e8a41f7)


### Potential alternatives

keep it as it is

### Additional information

_No response_
user: muendlein
issue_13184: state: open
title: [FEATURE] datetime formatting: respect localized timezone
body: ### Problem description

Not sure if this is a bug or a missing feature.

Currently localized timezones are converted to UTC for display. From a user point this is rather unexpected despite still being technically correct. The main issue is that data view (pandas dataframes) does not align with the plot view.
Additionally as a consequence the timezone format option (%z) will always show UTC/+0000 as of right now -> not sure if this is intended?

```
from bokeh.plotting import figure

import numpy as np

t = pd.date_range("2023-06-01", periods=11).tz_localize("US/Pacific")
print(t)
y = np.linspace(0, 10, 11)

fig = figure(x_axis_type="datetime")
fig.line(t, y)
fig.xaxis.formatter=DatetimeTickFormatter(
        minutes =["%H:%M"],
        hours=["%d-%m-%Y%z %H:%M"],
        days=["%d-%m-%Y%z %H:%M"],
        months=["%d-%m-%Y%z %H:%M"],
        years=["%d-%m-%Y%z %H:%M"],
    )

doc = curdoc()
doc.add_root(fig)
```

### Feature description

Show the localized time if the data is represented as localized time.

### Potential alternatives

-

### Additional information

![bokeh_feature_dt](https://github.com/bokeh/bokeh/assets/71090451/6d93e6bc-6a1d-4f58-9b0a-4798aa083099)

user: muendlein
issue_13180: state: open
title: [BUG] CrosshairTool is not linked with `overlay='auto'`
body: ### Expected behavior

I expected that the `CrosshairTool` is linked by default if it is used on multiple plots. This was working in older versions (2.x) of bokeh.

### Observed behavior

The `CrosshairTool` is linked if I don't use the default option and apply `Span`s to the `overlay` parameter.

```python
import numpy as np
from bokeh.layouts import gridplot
from bokeh.models import CrosshairTool, Span
from bokeh.plotting import figure, show, output_notebook
output_notebook()

width = Span(dimension="width", line_dash="dashed", line_width=2, line_color='magenta')
height = Span(dimension="height", line_dash="dotted", line_width=2, line_color='orange')

crosshair = CrosshairTool(overlay=[width, height])

children = []
for i in range(6):
    p = figure(width=200,height=200)
    p.line(np.arange(10), np.random.random(10))
    p.add_tools(crosshair)
    children.append(p)

show(gridplot(children=children, ncols=3))
```
![linked_corsshair](https://github.com/bokeh/bokeh/assets/68053396/78644120-17df-4913-8772-975823dd5f78)

If I remove the `Span`s from the `CrosshairTool` and use the default, the tool is only working for a single figure.

```python
import numpy as np
from bokeh.layouts import gridplot
from bokeh.models import CrosshairTool, Span
from bokeh.plotting import figure, show, output_notebook
output_notebook()

crosshair = CrosshairTool()

children = []
for i in range(6):
    p = figure(width=200,height=200)
    p.line(np.arange(10), np.random.random(10))
    p.add_tools(crosshair)
    children.append(p)

show(gridplot(children=children, ncols=3))
```
![not_linked_corsshair](https://github.com/bokeh/bokeh/assets/68053396/0f8da83f-658b-4b02-a6c8-a1f0ed5251e7)

It is not clear why this two versions behave differently.

<details><summary>Technical information</summary>
### Software versions

Python version      :  3.9.10 | packaged by conda-forge | (main, Feb  1 2022, 21:24:11) 
IPython version     :  8.4.0
Tornado version     :  6.1
Bokeh version       :  3.1.1
BokehJS static path :  /opt/conda/envs/aiva-env/lib/python3.9/site-packages/bokeh/server/static
node.js version     :  v17.4.0
npm version         :  8.3.1
Operating system    :  Linux-4.19.0-21-amd64-x86_64-with-glibc2.31

### Browser name and version

Chrome Version 114.0.5735.110

### Jupyter notebook / Jupyter Lab version

Selected Jupyter core packages...
IPython          : 8.4.0
ipykernel        : 6.22.0
ipywidgets       : 7.7.1
jupyter_client   : 7.1.2
jupyter_core     : 5.3.0
jupyter_server   : not installed
jupyterlab       : not installed
nbclient         : 0.5.12
nbconvert        : 6.4.2
nbformat         : 5.1.3
notebook         : 6.4.8
qtconsole        : 5.4.2
traitlets        : 5.9.0
</details>
user: mosc9575
issue_13179: state: open
title: Improve performance of bokehjs' build system
body: The existing build system is not adequate for efficient work on bokehjs anymore. This is an especially pronounced issue with doing rapid incremental changes. The main reason for its slowness is that it's based on JS tooling, in particular on tsc, which are inherently slow. Since introduction of this build system, the JS build ecosystem changed a lot and new performance minded tooling emerged. In particular swc (https://swc.rs/), a Rust-based JS/TypeScript/etc. compiler platform shows a lot of promise for being a basis for high performance and high flexibility JS tooling.

The goal of this work is to provide a drop-in replacement for the existing build system, so that older versions of bokehjs can be compiled with it as well. This build system will live in a separate project on GitHub (maybe as a part of a larger Rust-based bokeh tooling repository `bokehrs` (not yet on GitHub)). The build system will be precompiled for 3 major platforms (binary executables) and will be also provided as a WASI (non-browser WASM) binary (this will require external runtime like wasmer or wasmtime, but otherwise will be platform independent). The work will be spread across a longer time period, so right now it's not clear to me when the first usable version will be available.

For me this side project will be an opportunity to improve my Rust skills for future work on WASM in bokehjs.

user: mattpap
issue_13177: state: open
title: Remove message "User authentication hooks NOT provided (default user enabled)"
body: ### Problem description

In https://discourse.holoviz.org/t/cannot-execute-script-with-panel/5464 *NoBody* gets confused by the message `User authentication hooks NOT provided (default user enabled)` that is shown with `panel serve`. He thinks this is causing him not to be authenticated to read files on his server.

### Feature description

As authentication is not normally something new users think about I would propose 

- Not showing this message when the server is launched without authentication hooks
- Showing the message `User authentication hooks provided` when the server is started with authentication hooks.

### Potential alternatives

- Keep it
- Improve the message

### Additional information

Phillip Rudiger asked me to report this one with Bokeh in https://github.com/holoviz/panel/issues/5021
user: MarcSkovMadsen
labels: python: Issues that should only require updating Python code
issue_13172: state: open
title: [BUG] `server_session` example does not update plot title
body: ### Software versions

Python version      :  3.9.12 (main, Mar 26 2022, 15:52:10) 
IPython version     :  (not installed)
Tornado version     :  6.3.2
Bokeh version       :  3.1.1
BokehJS static path :  /Users/xxx/programmering/python/bokeh/server/flask_embed/.venv/lib/python3.9/site-packages/bokeh/server/static
node.js version     :  v16.14.2
npm version         :  8.5.0
Operating system    :  macOS-12.5-x86_64-i386-64bit


### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

When I run the [`server_session` example](examples/output/apis/server_session/serve.py) I had expected the title of the plot to get updated based on the code:
https://github.com/bokeh/bokeh/blob/cb2de9ea4b92e54f2fe4b3b4a460c6d49c762f53/examples/output/apis/server_session/serve.py#L17

### Observed behavior

Title of plot is not updated. The plot itself in `bokeh_app.py` is not initialized with a title text, but I have also tested that and do not observe that the text gets updated

### Example code

```Python
The example is located in bokeh/examples/output/apis/server_session/
```


### Stack traceback or browser console output

_No response_

### Screenshots


<img width="628" alt="Skærmbillede 2023-05-30 kl  18 24 51" src="https://github.com/bokeh/bokeh/assets/33606696/b61c958e-12c9-44d5-b1e2-0dea3794e7d9">

user: carve11
issue_13171: state: open
title: Improve naming conventions regarding types of tests
body: Specifically figure out a better name for "cross" integration tests introduced in PR #13137.
user: mattpap
issue_13168: state: open
title: Adding a HTML table to ColumnDataSource[FEATURE] 
body: ### Problem description

Regarding the conversation I had with @bryevdv, I discovered that when `source.data` is called, ColumnDataSource renders its data as a straightforward Python dictionary. To enhance the usability of ColumnDataSource, I propose the addition of a `.to_html()` method to the class. This method would generate an HTML table in Jupyter notebooks, allowing us to view the data in a structured manner similar to a pandas DataFrame.

### Feature description

The methods will look something like this:

```
from jinja2 import Template

def generate_html_table(source):
    # Get the data from the ColumnDataSource
    data = source.data

    # Get the column names
    column_names = list(data.keys())

    # Generate the table rows
    num_rows = len(data[column_names[0]])

    if num_rows > 20:
        max_length = 10
        rows_to_display = []
        for i in range(max_length):
            row = [data[col][i] for col in column_names]
            rows_to_display.append(row)

        # Add an ellipsis row
        rows_to_display.append(["..." for _ in column_names])

        for i in range(num_rows - max_length, num_rows):
            row = [data[col][i] for col in column_names]
            rows_to_display.append(row)
    else:
        rows_to_display = []
        for i in range(num_rows):
            row = [data[col][i] for col in column_names]
            rows_to_display.append(row)

    # Render the HTML table using Jinja2 template
    template = Template("""
        <table>
            <thead>
                <tr>{% for name in column_names %}<th>{{ name }}</th>{% endfor %}</tr>
            </thead>
            <tbody>
                {% for row in rows %}
                <tr>{% for item in row %}<td>{{ item }}</td>{% endfor %}</tr>
                {% endfor %}
            </tbody>
        </table>
    """)

    html_table = template.render(column_names=column_names, rows=rows_to_display)
    return html_table


from IPython.display import display, HTML

def to_html(self):
    html_table = generate_html_table(self)
    display(HTML(html_table))


from bokeh.models import ColumnDataSource as cds

# Add the custom method to the ColumnDataSource class
cds.to_html = to_html
```

The `generate_html_table()` method creates a table, showing only the first and last 10 rows if the rows are more than 20. The `to_html` method renders the table in Jupyter notebook.

A use case will look like this: `source.to_html()`

### Potential alternatives

We briefly explored the option of converting the data in ColumnDataSource (CDS) to a pandas DataFrame but we wanted a situation where it could also work without having to install pandas.

### Additional information

_No response_
user: Azaya89
issue_13164: state: open
title: [BUG] Broken link in Sphinx docs
body: ### Software versions

$ bokeh info
Python version      :  3.10.11 | packaged by conda-forge | (main, May 10 2023, 19:01:19) [Clang 14.0.6 ]
IPython version     :  8.13.2
Tornado version     :  6.3.2
Bokeh version       :  3.2.0.dev2+21.gbb3a6051.dirty
BokehJS static path :  /Users/cdeil/code/oss/bokeh/src/bokeh/server/static
node.js version     :  v18.15.0
npm version         :  9.6.7
Operating system    :  macOS-13.4-arm64-arm-64bit

### Expected behavior

At https://docs.bokeh.org/en/latest/docs/user_guide/basic/data.html#appending-data-to-a-columndatasource I would have expected that clicking on "stream" would link to https://docs.bokeh.org/en/latest/docs/reference/models/sources.html#bokeh.models.ColumnDataSource.stream

### Observed behavior

Nothing happens, the link isn't working.

### Notes

Probably the problem is that at https://github.com/bokeh/bokeh/blob/branch-3.2/docs/bokeh/source/docs/user_guide/basic/data.rst#appending-data-to-a-columndatasource you have
```
:func:`~bokeh.models.sources.ColumnDataSource.stream`
```
and it should be this instead?
```
:func:`~bokeh.models.ColumnDataSource.stream`
```

In the docs there are several such broken links to ColumnDataSource methods.

However this seems to work in other parts of the docs:
```
:class:`~bokeh.models.sources.ColumnDataSource`
```

It's weird that this issue doesn't generate a warning / error in the Sphinx build!?

I tried to apply the fix and test locally, alas I get another unrelated Sphinx build issue and can't at the moment.


### Screenshots

<img width="566" alt="Screenshot 2023-05-28 at 11 55 54" src="https://github.com/bokeh/bokeh/assets/852409/0a020d67-5db1-405a-a6f5-bb30800b59cc">

user: cdeil
issue_13163: state: open
title: [FEATURE] Improve step plot missing value handling
body: ### Problem description

I am plotting IoT sensor time series data using "step" and sometimes there's a missing measurement.
The matplotlib step function does what I'd naively expect - omit one horizontal line if there's one missing data point.
The bokeh step function behaves differently - it omits two horizontal lines left and right of the missing data point.

### Feature description

I would like to have a way to use `bokeh.plotting.step` so that it creates a plot with just one missing horizontal line how MPL does it (i.e. create an extra horizontal line compared to what bokeh does at the moment).

If it's too late to adopt the MPL semantics or they are not desired for some reason, I think it would be great if you offered a keyword argument allowing the caller to get the MPL NaN handling.

### Potential alternatives

Is there a workaround to achieve this?

### Additional information

<img width="418" alt="Screenshot 2023-05-28 at 09 31 22" src="https://github.com/bokeh/bokeh/assets/852409/0dbbad90-35c2-4fac-8d51-1f7442c4fd41">

<img width="463" alt="Screenshot 2023-05-28 at 09 32 11" src="https://github.com/bokeh/bokeh/assets/852409/25c7f55f-3c84-461b-a884-e131960bd067">

user: cdeil
issue_13158: state: open
title: [BUG] Range1d, figure with log scale unable to handle low/high <1
body: ### Software versions

Python version      :  3.8.15 | packaged by conda-forge | (default, Nov 22 2022, 08:42:03) [MSC v.1929 64 bit (AMD64)]
IPython version     :  8.7.0
Tornado version     :  6.2
Bokeh version       :  3.1.0
BokehJS static path :  C:\ProgramData\Anaconda3\envs\geo_env38v1\lib\site-packages\bokeh\server\static
node.js version     :  v18.12.1

### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

Instantiating a figure with a log scale and a defined low/high range, where the high is less than 1 and the low greater than 0. e.g. 1e-7 low and 1e-5 high.

### Observed behavior

Results in this error in the browser console:

![image](https://github.com/bokeh/bokeh/assets/58000181/b3df5aad-aed6-4b04-8553-a5a26d07351b)

If I remove the 'log' y_axis_type, the plot renders as expected, or if change `high` to be 1 or higher, the plot also renders as expected. 

Pretty sure this worked fine in previous versions but not sure when the regression happened.

### Example code

```Python
from bokeh.plotting import figure,show

low = 1e-7
high = 1e-5

fig = figure(tools=[], x_range = [0, 1], y_range = [low, high]
              ,y_axis_type='log'
             )
show(fig)
```


### Stack traceback or browser console output

_No response_

### Screenshots

_No response_
user: gmerritt123
issue_13156: state: open
title: Can't build documentation in release build CI workflow
body: Release build fails with:
```
sphinx.errors.SphinxWarning: The version switcher "https://docs.bokeh.org/switcher.json" file is malformed at least one of the items is missing the "url" or "version" key
```
(e.g. in https://github.com/bokeh/bokeh/actions/runs/5067042634/jobs/9097592138).

I presume, to work around this, `pydata_sphinx_theme` need to be pinned. However, why does this fail in release build, but not in regular CI involving documentation?
user: mattpap
issue_13155: state: open
title: [BUG] Point draw does not work if color is an numpy array
body: ### Software versions

``` yaml
Python version      :  3.11.3 | packaged by conda-forge | (main, Apr  6 2023, 08:57:19) [GCC 11.3.0]
IPython version     :  8.13.2
Tornado version     :  6.3.2
Bokeh version       :  3.1.1
BokehJS static path :  /home/shh/miniconda3/envs/holoviz/lib/python3.11/site-packages/bokeh/server/static
node.js version     :  v18.15.0
npm version         :  9.5.0
Operating system    :  Linux-6.2.6-76060206-generic-x86_64-with-glibc2.35
```

### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

I would expect it to work the same regardless if it is a numpy array or a list. 

### Observed behavior

When using this example: https://docs.bokeh.org/en/3.1.1/docs/examples/interaction/tools/point_draw.html

and converting the color to a numpy array: 
``` python
source = ColumnDataSource({
    'x': [1, 5, 9], 'y': [1, 5, 9], 'color': np.array(['red', 'green', 'yellow'])
})
```
the example stops working and gives the console warning: `[bokeh] data source has columns of inconsistent lengths`

### Example code

```Python
Code here: https://docs.bokeh.org/en/3.1.1/docs/examples/interaction/tools/point_draw.html

And change the color to a numpy array.
```


### Stack traceback or browser console output

_No response_

### Screenshots

_No response_
user: Hoxbro
issue_13141: state: open
title: Add support for generic `Tooltip` hover
body: This PR allows to attach a `Tooltip` instance to any target (UI model, HTML element, etc.) and display the tooltip on hover over the target. Note the API is a proof of concept and it very likely to change.

fixes #12802
user: mattpap
issue_13130: state: open
title: Add CustomJSTicker
body: The asymmetry of having a `CustomJSTIckFormatter` while not offering a corresponding `CustomJSTicker` has low-level bothered me for years. However the utility of having one has come up in a few different support questions latetly, so we should go ahead and add one. 

It should be fairly straightforward to adapt existing "CustomJS" code to be callable by `Ticker.get_ticks` (keeping in mind @mattpap current work to afford imports inside CustomJS code). The main questions to decide:

`Ticker.get_ticks` expects to return both major and minor tick locations:
```
export type TickSpec<T> = {
  major: T[]
  minor: T[]
}
```
Do we want to expose this fully to CustomJS code? Or just expect users to return major ticks? Or have some simpler path to return only major ticks in the common case?

Additionally `get_ticks` accepts a number of parameters:
```
get_ticks(data_low: number, data_high: number, range: Range, cross_loc: number):
```
Do we expose all of these to the CustomJS code or leave off some of the more esoteric ones like `cross_loc`. In any case how do we pass the data to user code, maybe `cb_data`?

cc @bokeh/dev
user: bryevdv
issue_13120: state: open
title: Sitemap gen improvements
body: Our sphinx extensions for sitemaps is a bit crufty. Some potential improvements:

* Use a jinja template instead of looping file writes
* less chatty, don't use a link iterator for every file, just a single log message normally, or maybe dump the final file on verbose
* Only generate sitemap for pre-release or full version

These changes should remove a lot of cruft from CI build logs, and maybe even a slight speed boost (excessive console logging in GH actions actually adds up)

Or ideally, maybe there is a good third party sphinx extension for sitemaps nowadays, and we can just delete ours entirely. 

cc @tcmetzger for thoughts

user: bryevdv
issue_13119: state: open
title: [BUG] Server CDS update with slider performance degraded
body: ### Software versions

Python version      :  3.9.16 (main, Jan 11 2023, 16:16:36) [MSC v.1916 64 bit (AMD64)]
IPython version     :  (not installed)
Tornado version     :  6.2
Bokeh version       :  3.1.1
BokehJS static path :  C:\Users\user\Anaconda3\envs\test\lib\site-packages\bokeh\server\static
node.js version     :  (not installed)
npm version         :  (not installed)
Operating system    :  Windows-10-10.0.19045-SP0

### Browser name and version

Google Chrome Version 113.0.5672.64 (Official Build) (64-bit)

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

On upgrade from Bokeh v3.0.3 to 3.1.0/3.1.1, no degradation in performance of updating the ColumnDataSource used for an image display using a slider when running the Bokeh Server.

### Observed behavior

The image update is noticeably slower. I think this is not just related to CDS patching as I've got similar results when using sliders for other purposes, e.g. changing the alpha value of an image.

### Example code

```Python
import numpy

from bokeh.models.sources import ColumnDataSource
from bokeh.models.widgets import Slider
from bokeh.models.layouts import Column
from bokeh.plotting import figure, show
from bokeh.io import curdoc

n = 800
x_all = numpy.random.rand(10, n, n)
x_im = x_all[0]
cds = ColumnDataSource(data={'x_all': [x_all], 'x_im': [x_im]})

def change_vals(attr, old, new):
    cds.patch({'x_im': [(0, cds.data['x_all'][0][new])]})

fig = figure(x_range=(0, n - 1), y_range=(0, n - 1), height=n, width=n)
fig.image('x_im', source=cds, x=0, y=0, dw=n, dh=n)

sld = Slider(title='Index', start=0, end=9, step=1, value=0)
sld.on_change('value', change_vals)

col = Column(fig, sld)
curdoc().add_root(col)
```


### Stack traceback or browser console output

_No response_

### Screenshots

_No response_
user: marcuskd
issue_13115: state: open
title: [BUG] `Bokeh.bokeh.embed_item` fails depending on order of keys in `item`
body: ### Software versions

Python version      :  3.11.3 (main, Apr 19 2023, 14:26:19) [Clang 14.0.3 (clang-1403.0.22.14.1)]
IPython version     :  8.12.0
Tornado version     :  6.3
Bokeh version       :  3.1.0
BokehJS static path :  /Users/yves/.pyenv/versions/3.11.3/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/bokeh/server/static
node.js version     :  v19.5.0
npm version         :  9.3.1
Operating system    :  macOS-13.3.1-arm64-arm-64bit

### Browser name and version

Google Chrome Version 113.0.5672.63 (Official Build) (arm64)

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

`Bokeh.bokeh.embed_item(item)` should handle all semantically equivalent `items` equally. In particular, JS/JSON objects assign no semantic meaning to the order of the keys, and thus the behaviour should not depend on the order of the keys.



### Observed behavior

Instead, depending on the order of the keys, `embed_item` either performs the expected action, or causes an `Uncaught (in promise) Error: reference p1028 isn't known`. It appears that the decoding machinery cannot handle "forward-references", in the sense that parts of a document may refer to other parts of a document which haven't been decoded yet at the time the reference is encountered.

### Example code

Take the [`json_item.py`](examples/output/apis/json_item.py) example, and add `sort_keys=True` to the `json.dumps` call in any of the `/plot` / `/plot2` routes. Accessing `127.0.0.1:5000/` of the Flask app will now produce a broken web-page with one or both plots missing (depending on where `sort_keys` is applied), and a corresponding error in the developer console - at least on my version of Chrome.

Complete modified example:
<details>

```Python
import json

from flask import Flask
from jinja2 import Template

from bokeh.embed import json_item
from bokeh.plotting import figure
from bokeh.resources import CDN
from bokeh.sampledata.iris import flowers

app = Flask(__name__)

page = Template("""
<!DOCTYPE html>
<html lang="en">
<head>
  {{ resources }}
</head>

<body>
  <div id="myplot"></div>
  <div id="myplot2"></div>
  <script>
  fetch('/plot')
    .then(function(response) { return response.json(); })
    .then(function(item) { return Bokeh.embed.embed_item(item); })
  </script>
  <script>
  fetch('/plot2')
    .then(function(response) { return response.json(); })
    .then(function(item) { return Bokeh.embed.embed_item(item, "myplot2"); })
  </script>
</body>
""")

colormap = {'setosa': 'red', 'versicolor': 'green', 'virginica': 'blue'}
colors = [colormap[x] for x in flowers['species']]

def make_plot(x, y):
    p = figure(title = "Iris Morphology", sizing_mode="fixed", width=400, height=400)
    p.xaxis.axis_label = x
    p.yaxis.axis_label = y
    p.circle(flowers[x], flowers[y], color=colors, fill_alpha=0.2, size=10)
    return p

@app.route('/')
def root():
    return page.render(resources=CDN.render())

@app.route('/plot')
def plot():
    p = make_plot('petal_width', 'petal_length')
    return json.dumps(json_item(p, "myplot"), sort_keys=True)

@app.route('/plot2')
def plot2():
    p = make_plot('sepal_width', 'sepal_length')
    return json.dumps(json_item(p))

if __name__ == '__main__':
    app.run()
```

</details>

### Stack traceback or browser console output

The full stack-trace of the exception:
```
bokeh-3.1.0.min.js:211 Uncaught (in promise) Error: reference p1028 isn't known
    at p.error (bokeh-3.1.0.min.js:211:4951)
    at p._decode_ref (bokeh-3.1.0.min.js:211:4572)
    at p._decode (bokeh-3.1.0.min.js:211:1046)
    at p._decode_plain_object (bokeh-3.1.0.min.js:211:2340)
    at p._decode (bokeh-3.1.0.min.js:211:1066)
    at p._decode_object_ref (bokeh-3.1.0.min.js:211:4846)
    at p._decode (bokeh-3.1.0.min.js:211:1779)
    at bokeh-3.1.0.min.js:211:2249
    at f (bokeh-3.1.0.min.js:178:450)
    at p._decode_plain_array (bokeh-3.1.0.min.js:211:2237)
```

### Screenshots

_No response_
user: burnpanck
issue_13111: state: open
title: [FEATURE] - CustomJSTransform on DataTable
body: ### Problem description

I was recently tasked with adding a column to a table that is a direct derivative calculation of other values in its underlying CDS. I thought maybe I'd be be able to create a TableColumn that runs off a CustomJSTransform based transform.

Something like...

 ```
from bokeh.models import TableColumn, DataTable, ColumnDataSource,CustomJSTransform
from bokeh.transform import transform
from bokeh.plotting import show
src = ColumnDataSource(data={'x':[2,3,5]})
tr = CustomJSTransform(v_func=''' return xs.map(x=>x*2)''')


c1 = TableColumn(field='x',title='x')
# c2 = TableColumn(field=transform(field_name='x',transform=tr),title='x*2') #NOPE
tbl = DataTable(columns=[c1
                         # ,c2
                         ],source=src)

show(tbl)
```

### Feature description

The idea would be some sort of implementation that allows TableColumns to "understand" transform inputs the same way glyphs do.

### Potential alternatives

What I'm doing now is simply performing the calculation via CustomJS on DocumentReady, and adding a new column to the CDS that a TableColumn is prewired to use.

### Additional information

Originally made this as a discourse topic --> https://discourse.bokeh.org/t/customjstransform-on-tablecolumn/10422

user: gmerritt123
issue_13110: state: open
title: [BUG] CDS updated from DataTable fix input value in callback error
body: ### Software versions

Python version      :  3.10.11 (tags/v3.10.11:7d4cc5a, Apr  5 2023, 00:38:17) [MSC v.1929 64 bit (AMD64)]
IPython version     :  (not installed)
Tornado version     :  6.2
Bokeh version       :  2.4.3
BokehJS static path :  C:\Appl\env\buildup_auto_qc_310\lib\site-packages\bokeh\server\static
node.js version     :  (not installed)
npm version         :  (not installed)
Operating system    :  Windows-10-10.0.19045-SP0

panel                      : 0.14.4

### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

Example is in a panel app but this sounds like a bokeh issue as I am not using any panel widgets as such.
Added panel version in environment information in case.

Current app has 1 ColumnDataSource with some dummy Time - Value pairs.
Those are shown both in an editable DataTable and a crossplot which has PointEditTool setup.

The important point is that the "Time" valid value are limited to match a given time step of 0.5.
So when the user drag a point on the figure and change Time value in the table, there is callback which automatically find the modified values and snap their Time value to a valid value.

### Observed behavior

Dragging points on the plot and getting the Time value fixed to a valid value on the fly works fine

However when modifying a Time value in the DataTable, the refresh ends up in a crash
The app is still responsive after the error and it may look like it can be ignored. However everything is not fine.
Look at the color column green if "Time" > 3 else blue.

This Colour is updated in the callback function as well, so it is always synchronized with modified points "Time" values.

While editing the  "Time" value by dragging points on the plot, color is always updated.
While editing the "Time" value by changing it in the table, if the "Time" value is properly updated, the color does not updated.
So this error may end up with loosing some of the UI expected updates


### Example code

```Python

import bokeh
import panel as pn
import pandas as pd
import numpy as np

pn.extension()


def roundup_to_nearest(x: float, to_nearest: float) -> float:
    return x if np.isclose(x % to_nearest, 0.0) else x + to_nearest - x % to_nearest


def get_color(v: float) -> str:
    if v > 3:
        return "lightgreen"
    else:
        return "lightblue"


class AppWindow(pn.Column):
    def __init__(self):
        super().__init__()
        df = pd.DataFrame(
            {
                "Time": [1, 2.5, 3, 4.5, 5],
                "Value": [2.98, 4.6, 7.65, 4.56, 5.05],
            }
        )

        self.source = bokeh.plotting.ColumnDataSource(
            {
                "Time": df["Time"],
                "Value": df["Value"],
                "Color": [get_color(v) for v in df["Time"]],
            }
        )
        self.source.on_change("data", self.source_updated)

        columns = [
            bokeh.models.TableColumn(
                field="Time",
                formatter=bokeh.models.NumberFormatter(format="0.0"),
            ),
            bokeh.models.TableColumn(
                field="Value",
                formatter=bokeh.models.NumberFormatter(format="0.00"),
            ),
            bokeh.models.TableColumn(
                field="Color",
                formatter=bokeh.models.HTMLTemplateFormatter(
                    template=f"""
                                <div style="background:<%= Color %>; color: black">
                                <%= value %>
                                </div>
                            """
                ),
                sortable=False,
            ),
        ]

        table = bokeh.models.DataTable(
            source=self.source,
            columns=columns,
            editable=True,
        )

        tools = [
            bokeh.models.PanTool(),
            bokeh.models.BoxZoomTool(),
            bokeh.models.WheelZoomTool(),
            bokeh.models.ResetTool(),
        ]

        fig = bokeh.plotting.figure(
            title="Value vs Time",
            x_axis_label="Time",
            y_axis_label="Value",
            tools=tools,
            sizing_mode="stretch_both",
            toolbar_location="above",
        )

        render = fig.scatter(
            x="Time",
            y="Value",
            size=20,
            source=self.source,
        )

        pdt = bokeh.models.PointDrawTool(
            renderers=[render],
            empty_value="",
            add=False,
        )
        fig.add_tools(pdt)

        self.append(table)
        self.append(pn.pane.Bokeh(fig))

    def source_updated(self, attr, old, new):
        try:
            # remove callback to avoid infinite loop
            self.source.remove_on_change("data", self.source_updated)
        except (KeyError, ValueError):
            pass

        # time values only valid at specific interval of 0.5
        # so if user entered inbetween time we snap the point
        # to closest valid time value above selected
        df = pd.DataFrame(self.source.data)
        df = df.astype(dtype={"Time": "float64", "Value": "float64"})

        invalid_times_indexes = []
        for i, row in df.iterrows():
            if not np.isclose(row["Time"] % 0.5, 0.0):
                invalid_times_indexes.append(i)

        for invalid_times_index in invalid_times_indexes:
            invalid_time = df["Time"][invalid_times_index]
            closest_valid_value = roundup_to_nearest(x=invalid_time, to_nearest=0.5)
            print(
                invalid_time, " => closest above valid time is : ", closest_valid_value
            )
            df.iloc[
                invalid_times_index,
                df.columns.get_loc("Time"),
            ] = closest_valid_value

        self.source.data = {
            "Time": df["Time"],
            "Value": df["Value"],
            "Color": [get_color(v) for v in df["Time"]],
        }

        # reset callback
        self.source.on_change("data", self.source_updated)


def create_app():
    webbapp = AppWindow()

    return webbapp


if __name__ == "__main__":
    APP_ROUTES = {"app": create_app}

    pannel_server = pn.serve(
        APP_ROUTES,
        port=8000,
        start=True,
        show=False,
        autoreload=True,
        dev=True,
    )

    pannel_server.start()
    pannel_server.io_loop.start()
```


### Stack traceback or browser console output

(test_env_310) C:\Users\XXX\test>C:/Appl/env/test_env_310/Scripts/python.exe c:/Users/XXX/test/test_fix_user_input_CDS.py
Launching server at http://localhost:8000
4.5435345  => closest above valid time is :  5.0
C:\Appl\env\test_env_310\lib\site-packages\numpy\core\numeric.py:2463: FutureWarning: elementwise comparison failed; returning scalar instead, but in the future will perform elementwise comparison
  return bool(asarray(a1 == a2).all())
ERROR:bokeh.server.protocol_handler:error handling message
 message: Message 'PATCH-DOC' content: {'events': [{'kind': 'ColumnsPatched', 'column_source': {'id': '1002'}, 'patches': {'Time': [[3, '4.5435345']]}}], 'references': []}
 error: RuntimeError('Set changed size during iteration')
Traceback (most recent call last):
  File "C:\Appl\env\test_env_310\lib\site-packages\bokeh\server\protocol_handler.py", line 97, in handle
    work = await handler(message, connection)
  File "C:\Appl\env\test_env_310\lib\site-packages\bokeh\server\session.py", line 95, in _needs_document_lock_wrapper
    result = func(self, *args, **kwargs)
  File "C:\Appl\env\test_env_310\lib\site-packages\bokeh\server\session.py", line 289, in _handle_patch
    message.apply_to_document(self.document, self)
  File "C:\Appl\env\test_env_310\lib\site-packages\bokeh\protocol\messages\patch_doc.py", line 115, in apply_to_document
    invoke_with_curdoc(doc, lambda: doc.apply_json_patch(self.content, setter))
  File "C:\Appl\env\test_env_310\lib\site-packages\bokeh\document\callbacks.py", line 408, in invoke_with_curdoc
    return f()
  File "C:\Appl\env\test_env_310\lib\site-packages\bokeh\protocol\messages\patch_doc.py", line 115, in <lambda>
    invoke_with_curdoc(doc, lambda: doc.apply_json_patch(self.content, setter))
  File "C:\Appl\env\test_env_310\lib\site-packages\bokeh\document\document.py", line 391, in apply_json_patch
    DocumentPatchedEvent.handle_json(self, event_json, references, setter)
  File "C:\Appl\env\test_env_310\lib\site-packages\bokeh\document\events.py", line 259, in handle_json
    handler(doc, event_json, references, setter)
  File "C:\Appl\env\test_env_310\lib\site-packages\bokeh\document\events.py", line 767, in _handle_json
    source.patch(patches, setter)
  File "C:\Appl\env\test_env_310\lib\site-packages\bokeh\models\sources.py", line 721, in patch
    self.data._patch(self.document, self, patches, setter)
  File "C:\Appl\env\test_env_310\lib\site-packages\bokeh\core\property\wrappers.py", line 489, in _patch
    self._notify_owners(old, hint=ColumnsPatchedEvent(doc, source, patches, setter))
  File "C:\Appl\env\test_env_310\lib\site-packages\bokeh\core\property\wrappers.py", line 168, in _notify_owners
    for (owner, descriptor) in self._owners:
RuntimeError: Set changed size during iteration
C:\Appl\env\test_env_310\lib\site-packages\bokeh\server\protocol_handler.py:101: RuntimeWarning: coroutine 'WSHandler.send_message' was never awaited
  work = connection.error(message, repr(e))

### Screenshots

_No response_
user: RaphaelRosset
issue_13107: state: open
title: [FEATURE] DataTable: Provide access to the column index of selected cell
body: ### Problem description

In bokeh DataTable, it is possible to get access to the row number of the selected cell through the attribute .source.selected.indices of the DataTable.
For instance, it is possible to trigger actions with:
data_table.source.selected.on_change("indices", callback_function)

However, bokeh has currently no access to the column index of the selected cell.
One could say that bokeh is half blind about the position of the selected cell.

### Feature description

Provide access to the column index of the selected cell(s), for instance as an additional attribute to data_table.source.selected called "column_indices".
This could ideally be used directly in python without the need of a CustomJS function.
If ColumnDataSource doesn't support this attribute, maybe introducing this property at the DataTable level could be an alternative way?

This feature should work reliably with DataTable with scroll bars for both rows and columns.

### Potential alternatives

Other users proposed CustomJS alternatives:
[https://discourse.bokeh.org/t/how-to-get-the-column-index-of-a-selected-cell-in-a-scroll-datatable/10409](https://discourse.bokeh.org/t/how-to-get-the-column-index-of-a-selected-cell-in-a-scroll-datatable/10409)
[https://stackoverflow.com/questions/55964945/chart-on-click-selection-from-data-table-in-bokeh/56018835#56018835](https://stackoverflow.com/questions/55964945/chart-on-click-selection-from-data-table-in-bokeh/56018835#56018835)
[https://stackoverflow.com/questions/54426404/bokeh-datatable-return-row-and-column-on-selection-callback](https://stackoverflow.com/questions/54426404/bokeh-datatable-return-row-and-column-on-selection-callback)
[https://stackoverflow.com/questions/56271790/js-code-to-reference-row-column-in-bokeh-datatable-erroring](https://stackoverflow.com/questions/56271790/js-code-to-reference-row-column-in-bokeh-datatable-erroring)

Though, I encounter following problems with these CustomJS approach:
With Python 3.9 and bokeh 2.4.2, I can run the proposed CustomJS scripts, but the column (and row) index get messed up as soon as I use the scroll bars of the table.

With Python 3.11. and bokeh 3.1.0, I didn’t manage to run the JS code.

### Additional information

It would also be nice if a click on the column headers could be detected and registered as such.

Since DataTable allows a multiple selection, a further implementation of this feature could allow the access to a list of (row_index, column_index) pairs of all selected cells.
user: Icoti
issue_13102: state: open
title: [BUG] unexpected ‘hover_line_*’ option behaviour with multi_line
body: ### Software versions

Python version      :  3.11.3 | packaged by conda-forge | (main, Apr  6 2023, 09:05:00) [Clang 14.0.6 ]
IPython version     :  8.12.0
Tornado version     :  6.3
Bokeh version       :  3.1.0
BokehJS static path :  /Users/nunzio/mambaforge3/envs/data_analysis/lib/python3.11/site-packages/bokeh/server/static
node.js version     :  (not installed)
npm version         :  (not installed)
Operating system    :  macOS-12.4-x86_64-i386-64bit

### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

` hover_line_color`, ` hover_line_alpha`, etc. should allow highlighting any `multi_line` glyphs generated from a given `source` when using `HoverTool`

### Observed behavior

only the last rendered glyph seem to be affected by ` hover_line_color`, ` hover_line_alpha` options

### Example code

```Python
from bokeh.models import ColumnDataSource, HoverTool, IndexFilter, CDSView
from bokeh.plotting import figure, show

xs = [[0, 1]]*4
ys = [[1, 1], [2,2],[3, 3], [4,4]]

dd = dict(xs=xs, ys=ys)

source = ColumnDataSource(dd)

filt1 = IndexFilter(indices = [i for i in range(len(source.data['ys'])) if source.data['ys'][i][0] < 2.5])
filt2 = IndexFilter(indices = [i for i in range(len(source.data['ys'])) if source.data['ys'][i][0] > 2.5])

view1 = CDSView(filter = filt1)
view2 = CDSView(filter = filt2)

s = figure(tools=[HoverTool()])

g = s.multi_line(xs = 'xs', ys = 'ys', line_width=2, line_alpha=0.3, color = 'blue',
    hover_line_color='blue', hover_line_alpha=0.8, source = source, view = view1)

h = s.multi_line(xs = 'xs', ys = 'ys', line_width=2, line_alpha=0.3, color = 'red',
    hover_line_color='red', hover_line_alpha=0.8, source = source, view = view2)

show(s)
```


### Stack traceback or browser console output

_No response_

### Screenshots

_No response_
user: nlosacco
issue_13101: state: open
title: [FEATURE] `resources.CDN.render()`: do not include `bokeh-mathjax-x.y.z.min.js` when not needed
body: ### Problem description

I noticed that this is the largest resource loaded as part of loading/rendering an HTML doc that contains a Bokeh plot:
```
  <script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-mathjax-3.1.0.min.js"></script>
```
It is ~500 kB compressed, ~1.8 MB uncompressed.

We use `bokeh.resources.CDN.render()` to include those `<script>` tags.

I was trying to understand if we actually need mathjax for displaying the plot in question or not. I was wondering if Bokeh tries to be smart about including this resource: does it only include it when needed? So, I was looking for

- General documentation on the question of dynamic assembly. Found no answer (maybe I wasn't looking properly).
- Then I was looking for API docs -- I thought I can maybe provide an argument to `render()` and somehow tell it to toggle including mathjax or so. Found no pointers.

I then assumed that most probably every Bokeh plot depends on this resource.

For testing, I manually inserted the JavaScript resources and left the mathjax one out. The plot renders fine. I think!

For the purpose of creating this ticket I assume that _not_ every plot requires this resource. And I wonder how Bokeh developers think about all this.



### Feature description

Aiming high: The (maybe utopic) feature request would be do make  `bokeh.resources.CDN.render()` do the right thing by default: include mathjax when needed, and not when not needed. Or maybe we can provide a `include_mathjax=False` method with corresponding docs?




### Potential alternatives

Minimal solution: I think potentially (if my assumption is correct) we may want to at least add a statement somewhere in the documentation where we recommend developers to try not including mathjax (if they care about bandwidth usage / page load latency etc).

### Additional information

Maybe I missed something here entirely. Curious about your feedback! Thanks!
user: jgehrcke
issue_13097: state: open
title: Allow to generate self-contained SVG from `SaveTool` and `export_svg()`
body: ### Problem description

I am generating an .svg file of a bokeh plot with map tiles from OpenStreetMap. This .svg references the map tiles via links, such that it is only possible to render the .svg correctly in a web browser (and an internet connection). However, in other applications such as for example PowerPoint the map tiles can not be rendered (at least on the system I am working on) and therefore the original .svg file only renders without the tiles.

### Feature description

It would be great to have the option to download and embed the image tiles (as files) in the .svg file in the above example via the export_svgs() function.

### Potential alternatives

This code does what I want. Maybe you could add something like this as a feature to bokeh's export_svg() function?

```
import base64
import requests
import xml.etree.ElementTree as ET

def svg_embed_images(svg_file_path, output_file_path):
    # Parse the SVG file
    tree = ET.parse(svg_file_path)
    root = tree.getroot()
    xlink_namespace = "http://www.w3.org/1999/xlink"

    # Iterate through image elements and replace the href with base64-encoded data
    for image_element in root.iter("{http://www.w3.org/2000/svg}image"):
        # Get the image URL from xlink:href or href attribute
        image_url = image_element.attrib.get("{" + xlink_namespace + "}href") or image_element.attrib.get("href")
        
        
        if not image_url or 'data:image' in image_url:
            continue

        # Download and encode the image
        response = requests.get(image_url)
        mime_type = response.headers.get("content-type")
        image_data = base64.b64encode(response.content).decode("utf-8")

        # Replace the href with the base64-encoded data URI
        data_uri = f"data:{mime_type};base64,{image_data}"
        image_element.set("{" + xlink_namespace + "}href", data_uri)
        image_element.set("href", data_uri)

    # Save the modified SVG
    tree.write(output_file_path, encoding="utf-8", xml_declaration=True)

# Usage
svg_embed_images("input.svg", "output.svg")

```

### Additional information

For some reason the behavior of export_svg() seems OS dependent. On MacOS images are embedded via the function while on Windows 10 they are not. I am not sure why this is.
user: sebhan2
issue_13095: state: open
title: [BUG] TypeError with row and file_html
body: ### Software versions

Python 3.11.3
Bokeh 3.1.0
mypy 1.2.0

### Browser name and version

Brave@latest

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

I am returning to a project that uses bokeh, and I am getting some persistent mypy errors. Although I'm getting these errors, the code I am using works as I expect it to, and I am getting the plots I expect too. I cross checked everything with the [docs](https://docs.bokeh.org/en/latest/docs/reference/layouts.html#row), but can't figure out how to squash these type errors. 

### Observed behavior

mypy errors:

....py:102: error: Unexpected keyword argument "sizing_mode" for "row"  [call-arg]
....py:102: error: Argument 1 to "file_html" has incompatible type "row"; expected "Union[Model, Document, Sequence[Model]]"  [arg-type]
....py:103: error: List item 0 has incompatible type "figure"; expected "Union[row, col]"  [list-item]
...:104: error: List item 1 has incompatible type "figure"; expected "Union[row, col]"  [list-item]

### Example code

```Python
from bokeh.embed import file_html		# convert plot to html
from bokeh.layouts import row                   # horizontal plot
from bokeh.plotting import figure		# plot a figure
from bokeh.resources import CDN			# minified bokeh

truth_fig = figure(height=300, width=300, title='Ground Truth')
pred_fig = figure(height=300, width=300, title='Prediction')
    plot_settings = {
    'fill_color': '#1B9E31',
    'line_color': '#126B21',
    'x': 0.,
    'y': 0.,
}
truth_fig.circle(radius=y, **plot_settings)
pred_fig.circle(radius=y_hat, **plot_settings)
file_html(row(children=[
    truth_fig,
    pred_fig,
], sizing_mode='scale_width'), CDN, 'Drum Example.')),
```


### Stack traceback or browser console output

_No response_

### Screenshots

_No response_
user: lewiswolf
issue_13093: state: open
title: [FEATURE] Improve the interactivity by using mouse and keyboard
body: ### Problem description

I just read the tutorial from holoviz. In chapter 2, when [using `widget` mode for matplotlib](https://holoviz.org/tutorial/Plotting.html#using-pandas-plot), you can use left click button to pan and use right click button to zoom, where bokeh can't.

It's very inefficient to review and study many plots while you need to switch the control mode in the toolbar. For example, if you want to pan first and zoom in, and then pan, and the zoom in... to focus on specific data, you need to click the toolbar multiple times to switch between pan/zoom mode for mouse left click.


### Feature description

What if:

- When the mouse cursor is **hover over the plot** and **holding `shift`**, 
    - you can **zoom in/out** by **mouse wheel** (zoom-in center is where your mouse cursor is, just like Google Maps).
    - you can **select a box to zoom in** by **mouse left clicking and dragging**.
    - you can **undo** previous action by **mouse right clicking** (you can right click multiple times till the plot is reset to original view).
- When the mouse cursor is **hover over the plot** and **without holding `shift`**, 
    - you can **pan the plot** by **mouse left clicking and dragging**.
    - you can **zoom in and center the plot where you choose** by **DOUBLE mouse left clicking** (just like Google Maps).
    - scrolling the webpage by just **mouse wheel** as default.


### Potential alternatives

For non-wheel mouses or preferring using keyboard, you also can use the keystroke to zoom in/out and pan:

- When the mouse cursor is **hover over the plot** and **without holding `shift`**, 
    - you can **pan the plot** by keystroke **WASD** (up/left/down/right) about moving 25%~33% of plotting view size for each keystroke.
- When the mouse cursor is **hover over the plot** and **holding `shift`**, 
    - you can **pan the plot faster** by keystroke **WASD** (up/left/down/right) about moving 66% of plotting view size for each keystroke.
    - you can **zoom in/out** by keystroke **E/Q** (zoom-in center is where your mouse cursor is, just like Google Maps).
    - you can **undo** previous action by **R** (you can do this multiple times till the plot is reset to original view).



### Additional information

_No response_
user: xareelee
issue_13092: state: open
title: [BUG] one CDSView (still) can't be applied to two different ColumnDataSources
body: ### Software versions

Python version      :  3.8.15 | packaged by conda-forge | (default, Nov 22 2022, 08:42:03) [MSC v.1929 64 bit (AMD64)]
IPython version     :  8.7.0
Tornado version     :  6.2
Bokeh version       :  3.1.0
BokehJS static path :  C:\ProgramData\Anaconda3\envs\geo_env38v1\lib\site-packages\bokeh\server\static
node.js version     :  v18.12.1
npm version         :  8.19.2
Operating system    :  Windows-10-10.0.19044-SP0

### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

With 3.x, an advertised new feature is that a CDSView no longer needs a source argument, and will infer the underlying data source on which to construct a filter from. With that in mind, I made a single CDSView that contains a single GroupFilter in it, then tried to apply it on two renderers being driven by two different CDS's. 

### Observed behavior

This results in a size mismatch error. However I can eliminate the error and get the expected behaviour if I make two separate CDSViews, both pointing to the same GroupFilter and then assign them each to their respective renderer/CDS e.g. : 
```
bananaViewA = CDSView(filter=bfilt)
bananaViewB = CDSView(filter=bfilt)

figA = figure(x_range=srcA.data['fruit'])
figA.scatter(x='fruit',y='value',source=srcA,view=banana_viewA)

figB = figure(x_range=srcB.data['person'])
figB.scatter(x='person',y='rank',source=srcB,view=banana_viewB)
```



### Example code

```Python
from bokeh.plotting import figure, save
from bokeh.models import GroupFilter, CDSView, ColumnDataSource
from bokeh.layouts import row
#make two datasources with overlapping column names
srcA = ColumnDataSource({'fruit':['banana','orange','apple'],'color':['yellow','orange','red'],'value':[9,2,3]})

srcB = ColumnDataSource({'person':['bob','neil','joni','bruce','jimi']
                         ,'fruit':['banana','orange','banana','apple','apple']
                         ,'rank':[1,5,6,2,3]})

#make a groupfilter for only bananas
bfilt = GroupFilter(column_name='fruit',group='banana')
banana_view = CDSView(filter=bfilt)

figA = figure(x_range=srcA.data['fruit'])
figA.scatter(x='fruit',y='value',source=srcA,view=banana_view)

figB = figure(x_range=srcB.data['person'])
figB.scatter(x='person',y='rank',source=srcB,view=banana_view)

save(row([figA,figB]),'filtering.html')
```

### Screenshots

![image](https://user-images.githubusercontent.com/58000181/233192280-062025b7-ff2b-4c1a-afd8-0bd6148cd8de.png)
user: gmerritt123
issue_13091: state: open
title: Improve reliability of async server tests
body: This PR attempts to resolve `Task was destroyed but it is pending!` issues, which are both annoying and can influence other tests (like in PR #13070). For now I dealt with `HTTPServer.close_all_connections()` not being awaited for and now I'm working on issues related to reuse of `IOLoop` in `pull_session()`. 

user: mattpap
issue_13083: state: open
title: [BUG]  broken CSS when using a templates folder
body: ### Software versions

Python version      :  3.10.9 | packaged by Anaconda, Inc. | (main, Mar  8 2023, 10:42:25) [MSC v.1916 64 bit (AMD64)]
IPython version     :  8.10.0
Tornado version     :  6.2
Bokeh version       :  3.1.0
BokehJS static path :  C:\Users\ser942\Anaconda3\envs\py310\lib\site-packages\bokeh\server\static
node.js version     :  (not installed)
npm version         :  (not installed)
Operating system    :  Windows-10-10.0.19045-SP0

### Browser name and version

Firefox 111.0.1

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

Bokeh servers using a `templates` folder with an `index.html` file and custom css file no longer correctly apply the css to bokeh models.

This can be avoided by using InlineStyleSheet models, but the use of a CSS file in a templates folder seems broken.

(related [discourse post ](https://discourse.bokeh.org/t/how-do-we-use-index-html-and-styles-css-for-widgets-on-a-bokeh-server/10206))

### Observed behavior

This is the output, the `div` css is picked up (even for the div inside the Tabs object) but the `.bk-tab.bk-active` has no effect. 
![image](https://user-images.githubusercontent.com/22297924/231304418-3442a2cd-af51-4bc1-bfb8-cfbc2b50b17f.png)

For this css to be applied it needs to be placed where an InlineStyleSheet object would place it:
![image](https://user-images.githubusercontent.com/22297924/231304832-b5c9af08-62dc-4509-acbb-e1077726e930.png)


### Example code

```Python
Directory structure


test_serve/
├── custom_start.py
└── templates
    ├── index.html
    └── styles.css
```

custom_start.py:

```
import os
from jinja2 import Environment, FileSystemLoader
from tornado.ioloop import IOLoop
from bokeh.application.handlers import FunctionHandler
from bokeh.application import Application
from bokeh.server.server import Server

from bokeh.io import curdoc
from bokeh.io.state import curstate

from bokeh.models import Div, TabPanel, Tabs
from bokeh.layouts import column, row

app_path = os.path.dirname(__file__)


def modify_doc(doc):
    env = Environment(loader=FileSystemLoader(os.path.join(app_path, "templates")))
    doc.template = env.get_template("index.html")

    curstate().document = doc

    test_div = Div(text="TEST")

    test_tabs = Tabs(tabs=[TabPanel(title="TEST", child=column(children=[Div(text="hello")]))])

    curdoc().add_root(row(children=[test_div, test_tabs]))


def start_server():
    """
    Launch the bokeh server and connect to it.
    """
    io_loop = IOLoop.current()
    bokeh_app = Application(FunctionHandler(modify_doc))

    server = Server({"/": bokeh_app}, io_loop=io_loop)
    server.start()

    io_loop.add_callback(server.show, "/")
    io_loop.start()


if __name__ == "__main__":
    start_server()

```

index.html:
```
{% extends base %}

{% block postamble %}
  <style type="text/css">
    {% include 'styles.css' %}
  </style>
{% endblock %}
```

styles.css:
```
div {
    background-color: orange;
}

.bk-tab.bk-active {
    background-color: red;
}
```

just run with `python custom_start.py`
```


### Stack traceback or browser console output

_No response_

### Screenshots

_No response_
user: rocheseb
issue_13080: state: open
title: [BUG] panel sizing issue in bokeh 3.1.0
body: ### Software versions

Python version      :  3.9.12 (main, Apr  5 2022, 01:53:17) 
IPython version     :  8.2.0
Tornado version     :  6.2
Bokeh version       :  3.1.0
BokehJS static path :  /Users/paul/opt/anaconda3/lib/python3.9/site-packages/bokeh/server/static
node.js version     :  v16.13.0
npm version         :  8.1.0
Operating system    :  macOS-10.16-x86_64-i386-64bit

### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

notebook

### Expected behavior

Produce tabs, each containing a grid of charts that fills the whole page. The code works in 2.4.3, but not after upgrading to 3.1.0

### Observed behavior

The charts are small and unreadable

### Example code

```Python
import numpy as np
import pandas as pd
from bokeh.models import ColumnDataSource
from bokeh.plotting import figure
from bokeh.layouts import gridplot, column, row, layout
from bokeh.io import show, output_notebook
from bokeh.models import TabPanel, Tabs
#from bokeh.models.widgets import Tabs, Panel

output_notebook()

dates = pd.date_range("20210101", periods=6)
df_1 = pd.DataFrame(np.random.randn(6, 4), index=dates, columns=list("ABCD"))
df_1.rename_axis('Date', axis=0, inplace=True)
source = ColumnDataSource(df_1)

s1 = figure()
s1.line(x='Date',
        y='A',
        source = source,
        color='green')

s2 = figure()
s2.line(x='Date',
        y='A',
        source = source,
        color='green')

s3 = figure()
s3.line(x='Date',
        y='A',
        source = source,
        color='green')

s4 = figure()
s4.line(x='Date',
        y='A',
        source = source,
        color='green')

grid_one = gridplot([[s1,s2], [s3,s4]], sizing_mode='stretch_both', merge_tools=False)
grid_two = gridplot([[s1,s2], [s3,s4]], sizing_mode='stretch_both', merge_tools=False)

tab1 = TabPanel(child=(layout(column(grid_one), sizing_mode='stretch_both')), title="Grid One")
tab2 = TabPanel(child=(layout(column(grid_two), sizing_mode='stretch_both')), title="Grid Two")
    

tabs = Tabs(tabs=[ tab1, tab2 ], css_classes=['custom_tab']) 

show(tabs)
```


### Stack traceback or browser console output

_No response_

### Screenshots


![image](https://user-images.githubusercontent.com/77036253/231013985-50dda26a-b88e-45dd-ac74-483a47be54b2.png)


user: Paul-Edward-C
issue_13079: state: open
title: More efficient way to append ColumnSourceData for the figure.multi_line plot?  [FEATURE] 
body: ### Problem description

I have a `figure.multi_line` plot and would like to grow each of the lines as new streaming data comes in.  The number of lines stays constant throughout the life of the plot.  However, it is awkward to update the associated `ColumnDataSource`.  At the moment, here is a minimal example:

```python
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource
from bokeh.io import curdoc
import math

fig = figure(title='multiline-stream')
three_lines = { 'x': [ [], [], [] ], 'y': [ [], [], [] ] }
cds = ColumnDataSource(three_lines, name='mls')
fig.multi_line(xs='x', ys='y', source=cds)

global_x = 0

def append_to_lines():
    global global_x
    cds = doc.get_model_by_name('mls')
    if cds is None:
        print(f'cds is None')
        return
    data = cds.data
    offsets = (2.3, 3.1, 0.5)
    for line in range(3):
        data['x'][line].append(global_x)
        data['y'][line].append(math.sin(offsets[line] + global_x))
    global_x += 1

    # This works - but, does it do a deepcopy of `data`?  If not, then I suppose it's fine but it seems
    # weird.
    cds.data.update(data)


def work():
    doc.add_next_tick_callback(append_to_lines)

doc = curdoc()
doc.add_root(fig)

doc.add_periodic_callback(work, 1000)
```


### Feature description

I would like to avoid a deepcopy when calling `ColumnDataSource.data.update`.  Perhaps a more natural way to express line data for a `figure.multi_line` would be a nice alternative as well.

### Potential alternatives

I have a minimal working example of an alternative.

Broadly speaking, my sense is that the intention behind the `ColumnDataSource.stream` method was for streaming data.  The most natural notion of a multi-line plot to "stream" data is not to add new lines, but to grow existing lines.  So, my proposal would be to have a second option for how the multi-line data actually consumes the data in `ColumnDataSource` from the server side such that this growing of lines works naturally with `ColumnDataSource.stream`.

### Additional information

_No response_
user: hrbigelow
labels: python: Issues that should only require updating Python code
issue_13077: state: open
title: [BUG] figures collapse for different sizing_mode
body: ### Software versions

Python version      :  3.10.8 (tags/v3.10.8:aaaf517, Oct 11 2022, 16:50:30) [MSC v.1933 64 bit (AMD64)]
IPython version     :  (not installed)
Tornado version     :  6.2
Bokeh version       :  2.4.3/3.1.0
node.js version     :  v16.15.0
npm version         :  8.5.5
Operating system    :  Windows-10-10.0.19045-SP0


### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

Using `stretch_width` or `scale_width` should not collapse figures.

### Observed behavior

Starting with version 3.0 (also present in the latest version) figures may collapse if `stretch_width` or `scale_width` are used for the `sizing_mode`. Note: This can also be observed in the documentation: https://docs.bokeh.org/en/latest/docs/user_guide/basic/layouts.html#multiple-objects
In case of `stretch_width`  the collapsed layout also stretches in the height direction which is rather weird.

2.4.3 Scale width
![bokeh_scale_width_2_4_3](https://user-images.githubusercontent.com/71090451/230958217-7062b40c-587b-4a48-b43f-29d3396e6800.PNG)

2.4.3 Stretch width
![bokeh_stretch_width_2_4_3](https://user-images.githubusercontent.com/71090451/230958354-9a325051-ca22-4a61-a150-ca635ef36513.PNG)

3.1.0 Scale width
![bokeh_scale_width_3_1_0](https://user-images.githubusercontent.com/71090451/230958411-c4730b77-8ebb-4610-8c57-fd59887498c4.PNG)

3.1.0 Stretch width
![bokeh_stretch_width_3_1_0](https://user-images.githubusercontent.com/71090451/230958486-03b8181c-b723-4bac-b9a1-6d42060c28d0.PNG)




### Example code

```Python
import numpy as np

from bokeh.io import curdoc
from bokeh.layouts import column, row
from bokeh.models import ColumnDataSource, Slider, TextInput
from bokeh.plotting import figure


# Set up plot
plot = figure(title="my sine wave",
              tools="crosshair,pan,reset,save,wheel_zoom",
              x_range=[0, 4*np.pi], y_range=[-2.5, 2.5], sizing_mode="stretch_width")

# Set up widgets
text = TextInput(title="title", value='my sine wave')
offset = Slider(title="offset", value=0.0, start=-5.0, end=5.0, step=0.1)
amplitude = Slider(title="amplitude", value=1.0, start=-5.0, end=5.0, step=0.1)
phase = Slider(title="phase", value=0.0, start=0.0, end=2*np.pi)
freq = Slider(title="frequency", value=1.0, start=0.1, end=5.1, step=0.1)


# Set up layouts and add to document
inputs = column(text, offset, amplitude, phase, freq)

curdoc().add_root(row(inputs, plot))
curdoc().title = "Sliders"
```


### Stack traceback or browser console output

_No response_

### Screenshots

_No response_
user: muendlein
issue_13076: state: open
title: [BUG] Bytes property comparisons not implemented
body: ### Software versions

Bokeh: 3.1.0

### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

When creating a Model with a `Bytes` property events should be triggered appropriately if/when the value changes.

### Observed behavior

The `Comparator` errors out because no comparison is defined for an object of type `ArrayBuffer`:

https://github.com/bokeh/bokeh/blob/5190f2455fa7115283e121c72d71e061cbffea08/bokehjs/src/lib/core/util/eq.ts#L81

### Example code

```Python
None
```


### Stack traceback or browser console output

> Uncaught Error: can't compare objects of type [object ArrayBuffer]


### Screenshots

_No response_
user: philippjfr
issue_13074: state: open
title: Add support for ESM bundles to resources and embedding
body: This is a very early WIP PR that attempts to make use of ESM bundles (formerly `*.esm{.min}.js`, now `*{.min}.mjs`) in embedding APIs. This will take awhile to complete, as handling of ES modules is vastly different from handling regular JS scripts, and our embedding APIs are unsuited for this.

fixes #12941
user: mattpap
issue_13073: state: open
title: [BUG] Header row in DataTable does not resize to fit multi-line column titles
body: ### Software versions

Python version      :  3.10.10 | packaged by conda-forge | (main, Mar 24 2023, 20:12:31) [Clang 14.0.6 ]
IPython version     :  8.12.0
Tornado version     :  6.2
Bokeh version       :  3.1.0
BokehJS static path :  .../venv/lib/python3.10/site-packages/bokeh/server/static
node.js version     :  (not installed)
npm version         :  (not installed)
Operating system    :  macOS-13.1-arm64-arm-64bit

### Browser name and version

NA

### Jupyter notebook / Jupyter Lab version

Selected Jupyter core packages... IPython          : 8.12.0 ipykernel        : 6.22.0 ipywidgets       : 8.0.6 jupyter_client   : 8.1.0 jupyter_core     : 5.3.0 jupyter_server   : 2.5.0 jupyterlab       : 3.6.3 nbclient         : 0.6.8 nbconvert        : 6.5.4 nbformat         : 5.7.0 notebook         : 6.5.3 qtconsole        : 5.4.2 traitlets        : 5.9.0

### Expected behavior

I expect the header row in a DataTable to resize to accommodate multi-line column titles.

### Observed behavior

Instead, only the first line of each multi-line column title is visible

### Example code

```Python
from bokeh.io import show, output_notebook
from bokeh.models import ColumnDataSource, TableColumn
from bokeh.models.widgets import DataTable

output_notebook()

def make_table():
    data = {
        "column_1": [1, 2, 3],
        "column_2": [4, 5, 6],
        "column_3": [7, 8, 9],
    }

    data_table = DataTable(
        source=ColumnDataSource(data), 
        columns=[TableColumn(field=col, title=col.replace('_', "<br>")) for col in data.keys()], 
        width=400, 
        height=280, 
    )
    return data_table

data_table = make_table()
show(data_table)
```


### Stack traceback or browser console output

No error, see screenshot below

### Screenshots

The second row of each column name is obscured because the header height does not increase:
![image](https://user-images.githubusercontent.com/119897815/230659178-d75364e5-7a26-46d0-a60d-070e49b0b2ba.png)

Note that this remains true even if I increase the row height:
![image](https://user-images.githubusercontent.com/119897815/230659267-c7c7ab79-8f51-47f5-af03-8e03547e4a11.png)

user: doug-wispr
issue_13063: state: open
title: Add support for `package.json`'s `exports` field to modules linker
body: This will resolve issues with bundling `eslint` in extensions' compiler (see https://github.com/bokeh/bokeh/issues/12480#issuecomment-1482223960).
user: mattpap
issue_13038: state: open
title: Reduce verboseness of server debug logs
body: Early WIP. Reduces verboseness of server logs and introduces color for better readability.

![image](https://user-images.githubusercontent.com/27475/228890128-388d760c-eb1d-4142-9c70-0d6a5a9b0652.png)

fixes #11074
user: mattpap
issue_13037: state: open
title: Bokeh - New Tool - InfoTool
body: ### Problem description

There is only one Tool "HelpTool" which can be used as hiddne description of what is in the chart. My idea is to have possibility to have second way. 

### Feature description

Hello,

I found helpful tool called "HelpTool" which actually can direct user to description page --> good. There is one Idea I have is to have similar tool which would actually contain just text with (links, formats ..) but this text would be scrolled from ToolBar over the chart to let user have description what is shown inside.

you can imagine the movement as in Powerpoint - Transition between slides - "Push" (or any different kind of Powerpoint transition). 

If ToolBar = "Below" --> transition goes up over chart
If ToolBar = "Right" --> transition goes left over chart
....

Please tak it into consideration 

### Potential alternatives

HelpTool

### Additional information

Powerpoint Transition list some page i found on internet - https://www.customguide.com/powerpoint/powerpoint-transitions

user: VASKOTechDesign
issue_13027: state: open
title: [FEATURE] RangeTool: Use different cursor icons for changing the range vs. dragging the range
body: ### Problem description

Example: https://docs.bokeh.org/en/3.1.0/docs/examples/interaction/tools/range_tool.html

Currently the RangeTool uses the same icon for changing and dragging the range. This makes it a bit difficult when trying to change the range because there is no visual feedback if the cursor is placed correctly, i.e. it can happen that you instead drag the whole range.

### Feature description

Use two different cursor icons to make it clear to the user which action will happen.

### Potential alternatives

-

### Additional information

_No response_
user: muendlein
labels: tag: tools: Related to the toolbar and individual tools
issue_13007: state: open
title: Bokeh - Legend - Mute text only
body: ### Problem description

Hello,

is there some parameter which will make alpha only for legend text and not text backround?

Normal:
![image](https://user-images.githubusercontent.com/50368892/226673559-d13d2a6b-b596-45db-a798-29da560e1605.png)

Muted:
![image](https://user-images.githubusercontent.com/50368892/226673597-40f29373-70e2-4ef3-b71e-e98e65c30472.png)

→ this cause inconsistence when background.alpha < 1

Thank you

Jan

### Feature description

Can you implement the Inactive_Alpha, Inactive_Color, ... parameter for text only?

### Potential alternatives

Dont know

### Additional information

_No response_
user: VASKOTechDesign
issue_12994: state: open
title: [BUG] Patch does not output under certain conditions
body: ### Software versions

``` yaml
Python version      :  3.10.9 | packaged by conda-forge | (main, Feb  2 2023, 20:20:04) [GCC 11.3.0]
IPython version     :  8.11.0
Tornado version     :  6.1
Bokeh version       :  3.1.0
BokehJS static path :  /home/shh/miniconda3/envs/holoviz/lib/python3.10/site-packages/bokeh/server/static
node.js version     :  v18.15.0
npm version         :  9.5.0
Operating system    :  Linux-6.2.0-76060200-generic-x86_64-with-glibc2.35
```

### Browser name and version

Google chrome

### Jupyter notebook / Jupyter Lab version

3.6.1

### Expected behavior

Always show the patch plot

### Observed behavior

The patch plot is only shown under certain conditions.

This seems also to have been a problem before 3.0.

### Example code

```Python
import numpy as np
from bokeh.io import curdoc, output_notebook, show
from bokeh.models import ColumnDataSource, Patch
from bokeh.plotting import figure

output_notebook()

n = 400  # Works with 500, but will not work with 400

x_half = np.arange(start=4.0, stop=1100.1, step=0.1524)[n:]
x = np.concatenate([x_half, x_half[::-1]])
source = ColumnDataSource(dict(x=x, y=np.zeros_like(x)))

plot = figure()
glyph = Patch(x="x", y="y", fill_color="#f46d43")
plot.add_glyph(source, glyph)

show(plot)
```


### Stack traceback or browser console output

``` console
Uncaught (in promise) RangeError: Maximum call stack size exceeded
    at sort (<anonymous>:23778:24)
    at sort (<anonymous>:23795:15)
    at sort (<anonymous>:23795:15)
    at sort (<anonymous>:23795:15)
    at sort (<anonymous>:23795:15)
    at sort (<anonymous>:23795:15)
    at sort (<anonymous>:23795:15)
    at sort (<anonymous>:23795:15)
    at sort (<anonymous>:23795:15)
    at sort (<anonymous>:23795:15)
```

### Screenshots

![image](https://user-images.githubusercontent.com/19758978/226301420-8fb3b1ae-1777-4182-89a4-5981ced91400.png)

![image](https://user-images.githubusercontent.com/19758978/226301478-733083ee-7623-4f38-89eb-99100aef49b8.png)

user: Hoxbro
issue_12978: state: open
title: [BUG] Figure - rendering artefact - line around figure
body: ### Software versions

Python version: 3.11.2
Bokeh version: 3.1.0
Operation System: Windows 10 (21H2)


### Browser name and version

Microsoft Edge version: 111.0.1661.41

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

Expectation is to have this "rendering artefact" not rendered.

### Observed behavior

Hello all,

I wanted to make consistant background look of my charts and found that Im not able to make it.

Figure has few basic tools:
1) Backgroud
2) Outline
3) Border

But even I set all of these  I cannot get rid of the line(marked by green in example) --> this is especially visible when:
1) you give background_alpha < 1 AND/OR
2) you do not show ais



### Example code

```Python
from bokeh.plotting import figure, show
from math import pi, sin, cos

plot = figure(title=None, width=500, height=500, x_range = [-4,4], match_aspect=True)

# BackGround Colors
plot.background_fill_color = "#2A2A2A"
plot.background_fill_alpha = 0.8

# Outline
plot.outline_line_color = "#2A2A2A"
plot.outline_line_alpha = 0
plot.outline_line_width = 1

# Borders
plot.border_fill_color = "#2A2A2A"
plot.border_fill_alpha = 0.8
plot.min_border_left = 10
plot.min_border_right = 10
plot.min_border_top = 10
plot.min_border_bottom = 10


# Main circle
plot.annular_wedge(
    x = 0, 
    y = 0, 
    inner_radius = 3, 
    outer_radius = 3.01,
    direction = "anticlock", 
    start_angle = 0,
    start_angle_units = "rad",    # "deg", "rad", "grad" and "turn".
    end_angle = 2 * pi,
    end_angle_units = "rad",      # "deg", "rad", "grad" and "turn".
    line_color = "black", 
    fill_color = "black")


show(plot)
```


### Stack traceback or browser console output

_No response_

### Screenshots

With - Axis
![image](https://user-images.githubusercontent.com/50368892/225902330-1d6deb27-90bd-48fc-92bf-0041659116be.png)

No - Axis
![image](https://user-images.githubusercontent.com/50368892/225902554-1fe43c90-4d9b-4020-82c0-1fbb7f1f9608.png)

user: VASKOTechDesign
issue_12971: state: open
title: Setting Toolbar.active_multi to a RangeTool raises error
body: ### Behavior

In the docs for the [Toolbar model](https://docs.bokeh.org/en/latest/docs/reference/models/tools.html#bokeh.models.Toolbar.active_multi) the section for `active_multi` says 

> Specify an active multi-gesture tool, for instance an edit tool or a range tool.

I expected that

```python
from bokeh.plotting import figure, show, output_notebook
from bokeh.models import  RangeTool, Range1d

output_notebook()

p = figure()
p.line(x=[1,2,3,4], y=[1,2,3,4])

range_tool = RangeTool(x_range=Range1d(1,2))
p.add_tools(range_tool)

p.toolbar.active_multi = range_tool
show(p)
```

is working code, which is indeed in bokeh 2.4.3, but in bokeh 3.1.0 I got the folllowing error message.

> ```ValueError: failed to validate Toolbar(id='p1008', ...).active_multi: expected an element of either Null, Auto or Instance(GestureTool), got RangeTool(id='p1058', ...)```

I want to mention, that changing the line `p.toolbar.active_multi = range_tool` to `p.toolbar.active_multi = 'auto'` is working fine.
I am not sure if this is a lack of documentation or wrong inheritance because `RangeTool` is based on `Tool`and not on `GestureTool`.

<details>
  <summary>technical information</summary>
### Software versions

Python version      :  3.9.10 | packaged by conda-forge | (main, Feb  1 2022, 21:24:11) 
IPython version     :  8.1.1
Tornado version     :  6.1
Bokeh version       :  3.1.0
BokehJS static path :  /opt/conda/envs/bk3/lib/python3.9/site-packages/bokeh/server/static
node.js version     :  v17.4.0
npm version         :  8.3.1
Operating system    :  Linux-4.19.0-21-amd64-x86_64-with-glibc2.31

### Browser name and version

Chrome Version 111.0.5563.65

### Jupyter notebook / Jupyter Lab version

IPython          : 8.1.1
ipykernel        : 6.9.1
ipywidgets     : 7.6.5
jupyter_client : 7.1.2
jupyter_core   : 4.9.2
jupyter_server : not installed
jupyterlab       : not installed
nbclient           : 0.5.12
nbconvert        : 6.4.2
nbformat         : 5.1.3
notebook         : 6.4.8
qtconsole         : not installed
traitlets             : 5.1.1
<details>
user: mosc9575
issue_12964: state: open
title: [FEATURE] Matplotlib ConnectionPatch functionality in Bokeh
body: ### Problem description

I work with image keypoint matching (i.e. SIFT) and I find very useful to visualize keypoints and matches.
When working with matplotlib, the best way for me to visualize them is to use two subplots, where in each subplot I plot the source image and the keypoints. I then plot the matches as lines between the subplots with the function ConnectionPatch.
The advantage of this approach, over plotting everything in a single plot, is that I can zoom-in in the two images independently, and matplotlib automatically only shows that matches for the current region.

Attached a screen of how the plot looks like when using two subplots and the ConnectionPatch function in Matplotlib.
![image](https://user-images.githubusercontent.com/10034153/225337622-0991ebfc-660b-4b7e-93e9-833e3b8ba27d.png)


### Feature description

The ConnectionPatch functionality in bokeh, where lines can be drawn between different subplots.

### Potential alternatives

Currently I plot everything in the same plot (images, keypoints and matches). 
But this does not allow me to zoom-in independently in the two images.

### Additional information

_No response_
user: santelelle
issue_12961: state: open
title: Add initial support for WebAssembly to bokehjs
body: This is a very early preview of WebAssembly support in bokehjs. It will be at least a month or two before this will be in any useable state. I will follow up with details when this is actually useable to any extent.
user: mattpap
issue_12958: state: open
title: [BUG] Save Tools includes extra borders
body: ### Software versions

Python version      :  3.10.9 (main, Dec 19 2022, 17:35:49) [GCC 12.2.0]
IPython version     :  8.10.0
Tornado version     :  6.2
Bokeh version       :  3.1.0
BokehJS static path :  /home/HIDDEN/.cache/pypoetry/virtualenvs/VENV-6qwQwmyu-py3.10/lib/python3.10/site-packages/bokeh/server/static
node.js version     :  v18.12.1
npm version         :  8.19.2
Operating system    :  Linux-6.2.5-arch1-1-x86_64-with-glibc2.37

### Browser name and version

Chrome

### Jupyter notebook / Jupyter Lab version

Jupyter Lab 3.6.1

### Expected behavior

We are using the `dark_minimal` theme.
When saving a plot using the save tool, an unwanted white border around the figure(top and left side) is visible. As shown in the first screenshot.

Also, when the figure is  resizeable, the save will include an extra padding on the bottom in white(replacing the scrollbar). As shown in the second screenshot.

We tried to use a custom theme to put these artifacts the same color as the background figure color but weren't able to find the parametrization(if it is feasible)

### Observed behavior

No white border on the top and left side
If the figure is resizable there shouldn't be anything at the bottom of the save figure

### Example code
The code used to generate these two images:

```Python
from bokeh.io import output_notebook, curdoc
from bokeh import plotting

import pandas as pd

output_notebook()
df = pd.DataFrame({"x": [1,2,3], "y": [4,6,7]})
```

To Generate the first image:
```
curdoc().theme = 'dark_minimal'
figure = plotting.figure()
figure.line(df['x'], df['y'])
plotting.show(figure)
```

To generate the second image
```
curdoc().theme = 'dark_minimal'
figure = plotting.figure()
figure.resizable = True
figure.line(df['x'], df['y'])
plotting.show(figure)
```


### Stack traceback or browser console output

_No response_

### Screenshots

![image](https://user-images.githubusercontent.com/94702659/225085634-a3800ad4-a363-4ac0-b43d-f7bfc4cfd4b6.png)
![image](https://user-images.githubusercontent.com/94702659/225085580-bccff04e-42b8-4ff7-9ccf-ded4143c08e0.png)
user: jmdeschenes
issue_12945: state: open
title: Overlap in palette docs
body: cc @tcmetzger @Soot3 

as noted in https://github.com/bokeh/bokeh/pull/12929#issuecomment-1464503396

> The viridis palette is considered accessible AFAIK, but it is listed under "Matplotlib Palettes" because that is where it was sourced from. What should we do here? We could duplicate the relevant palettes under "Accessible palettes" or just add a note with links to specific accessible palettes from other sections.
user: bryevdv
issue_12931: state: open
title: Options for finer-grained CI runs
body: Currently we run all CI jobs on every CI run on every push to the repo. This requires 30+ minute waits to see results even for trivial changes. I believe the following kinds of changes should be possible:

* automatically limiting jobs based on location of repo changes, e.g. if the only changes are under the `docs` directory, skip all the unit tests, etc. 
* explicit job control, e.g. via PR comments. These could be in the OP to skip or enable jobs that would otherwise not be, or to re-run later with a different set of jobs

Marking as a `discussion` for now since there is some investigation required up front. If anyone @bokeh/dev has experience or thoughts please chime in.
user: bryevdv
issue_12916: state: open
title: [BUG] updating table view causes the table to disappear
body: ### Software versions

Python version      :  3.10.8 (main, Nov  4 2022, 08:45:25) [Clang 12.0.0 ]
IPython version     :  8.8.0
Tornado version     :  6.2
Bokeh version       :  3.0.3
BokehJS static path :  /Users/ben/miniconda3/envs/image-labeller/lib/python3.10/site-packages/bokeh/server/static
node.js version     :  v16.13.1
npm version         :  8.1.2
Operating system    :  macOS-12.6-arm64-arm-64bit

### Browser name and version

firefox opera chrome

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

trying to dynamically update the table view by setting a DataTable's view, I expected it to update the view as happens in bokeh 2.4, but instead the table disappears entirely in 3.03. Not sure if this is expected because of user error or if this is a bug..

### Observed behavior

table no longer displays in browser after it updates

### Example code

```Python
import numpy as np
import pandas as pd
from bokeh.layouts import column
from bokeh.models import DataTable, ColumnDataSource, TableColumn, BooleanFilter, CDSView, IndexFilter
from bokeh.server.server import Server
from tornado.ioloop import IOLoop


class T:
    def __call__(self, doc):
        self.doc = doc
        data = pd.DataFrame(np.random.random([10, 10]))
        data.columns = [str(x) for x in data.columns]
        self.source = ColumnDataSource(data=data)
        cols = [TableColumn(field=x) for x in data.columns]

        self.table = DataTable(source=self.source, columns=cols)
        self.doc.add_root(column(self.table))
        self.doc.add_periodic_callback(self.rand_view, 5000)

    def rand_view(self):
        self.table.view = CDSView(filter=IndexFilter([np.random.randint(0, len(self.source.data))]))


if __name__ == '__main__':

    io_loop = IOLoop.current()
    app_factory = T()

    server = Server({"/": app_factory}, io_loop=io_loop, port=int(5006))
    server.start()

    io_loop.add_callback(server.show, "/")
    io_loop.start()
```


### Stack traceback or browser console output

_No response_

### Screenshots

_No response_
user: benduffy1
issue_12912: state: open
title: Example using categorical colormapping
body: We don't yet have a decent example, with explanation, of categorical colormapping that was added in #12356.
user: ianthomas23
issue_12907: state: open
title: Add `examples/plotting/css_classes_themed.py`
body: This is a variation of `css_classes.py` example. In the original, stylesheets are applied to individual models. Here, a "global" stylesheet is added via a theme. The effect at runtime is the same in both cases, but the later may be more appealing for users migrating from using pre shadow DOM global stylesheets.

The output is exactly the same between `css_classes.py` and `css_classes_themed.py`:

![image](https://user-images.githubusercontent.com/27475/223406366-be878752-e738-4da5-b0bf-13e4b7fd7c2a.png)

user: mattpap
issue_12904: state: open
title: [BUG] DatetimePicker improvements
body: ### Software versions

``` yaml
Python version      :  3.10.9 | packaged by conda-forge | (main, Feb  2 2023, 20:20:04) [GCC 11.3.0]
IPython version     :  8.11.0
Tornado version     :  6.1
Bokeh version       :  3.1.0.dev5
BokehJS static path :  /home/shh/miniconda3/envs/holoviz/lib/python3.10/site-packages/bokeh/server/static
node.js version     :  v18.13.0
npm version         :  8.19.3
Operating system    :  Linux-6.1.11-76060111-generic-x86_64-with-glibc2.35

```

### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

1) I would expect it to be a datetime (or a date).
2) The `min_date`/`max_date` ignore the timezone. 

### Observed behavior

Two problems I have observed: 

1) The value is a float. 
2) The `min_date`/`max_date` does not match the input, which probably is a timezone issue. 


### Example code

```Python
from datetime import date

from bokeh.io import curdoc
from bokeh.models import DatetimePicker

datetime_picker = DatetimePicker(
    title="Select date",
    value=date(2019, 10, 1),
    min_date=date(2019, 10, 1),  # Set to 2019-10-01 - 02:00 in the widget
    max_date=date(2019, 10, 30),  # Set to 2019-10-30 - 01:00 in the widget
)

print(datetime_picker.value)  # This is returning a float, would expect datetime (or date)

curdoc().add_root(datetime_picker)
```


### Stack traceback or browser console output

_No response_

### Screenshots

_No response_
user: Hoxbro
issue_12898: state: open
title: bokehjs' CI workflow on Windows timeouts on regular basis
body: It either fails to start headless browser, failing with:
```
failed: timeout starting c:\Program Files\Google\Chrome\Application\chrome.EXE
```
or a subset of tests (unit or integration) results in timeout. 
user: mattpap
issue_12893: state: open
title: [FEATURE] Serialize float16 ndarrays using base64 strings like other float dtypes?
body: ### Problem description

I use html files to save visualizations containing several images and a control widget.

In an effort to reduce the size of exported html files, I tried to work with `np.float16` arrays as precision is not really a concern here. To my surprise, the resulting file was larger than the file containing float64 data. This is because float64 (and float32) arrays are serialized to binary using base64 strings while float16 are represented in decimal form with their digits (_i.e._ like their `__repr__`).

Looking at the relevant code ([serialization.py](https://github.com/bokeh/bokeh/blob/branch-3.1/src/bokeh/core/serialization.py)), I can see that float16 arrays are not treated the same way float64/32 arrays are. The list of dtypes supported for binary format serialization is defined [here](https://github.com/bokeh/bokeh/blob/f6c2e6d980be12515e007e9c65f1f10e7bde7e37/src/bokeh/util/serialization.py#L70) and does not include `np.float16`.

Maybe there is a reason for this? I can see for instance that `np.int64` and `np.uint64` dtypes are commented out, so presumably there is a reason to exclude them.

### Feature description

If there is no specific reason to exclude `np.float16` from the supported dtypes, I imagine that it would be straightforward to add it. It would be more efficient, as it is for other dtypes.
user: TheoMathurin
issue_12878: state: open
title: Allow to use bokehjs' `*.d.ts` files without `skipLibCheck: true`
body: With `skipLibCheck: false` there are numerous type errors related to external libraries (e.g. `regl`, `MathJax`). Depending on the library, this can be solved by not exposing it in the public API (`regl`), including its type declarations as a dependency of bokehjs' package (see issue #12616), or propagating our own third-party declarations to bokehjs' package (`MathJax`).
user: mattpap
issue_12858: state: open
title: `test_export` is failing on OSX and Firefox
body: From PR #12852:
```
FAILED tests/unit/bokeh/io/test_export.py::test_get_screenshot_as_png[firefox-dimensions2] - Failed: Timeout >60.0s
FAILED tests/unit/bokeh/io/test_export.py::test_get_screenshot_as_png[firefox-dimensions3] - Failed: Timeout >60.0s
FAILED tests/unit/bokeh/io/test_export.py::test_get_screenshot_as_png[firefox-dimensions4] - Failed: Timeout >60.0s
FAILED tests/unit/bokeh/io/test_export.py::test_get_screenshot_as_png_with_glyph[firefox-dimensions2] - assert 0 == 17956
FAILED tests/unit/bokeh/io/test_export.py::test_get_screenshot_as_png_with_glyph[firefox-dimensions3] - assert 0 == 188356
FAILED tests/unit/bokeh/io/test_export.py::test_get_screenshot_as_png_with_glyph[firefox-dimensions4] - assert 0 == 2056356
```
Currently those tests are now skipped, to unblock the CI.
user: mattpap
issue_12849: state: open
title: Reduce the number of MacOS CI jobs
body: With new [usage limits](https://docs.github.com/en/actions/learn-github-actions/usage-limits-billing-and-administration#usage-limits) on GitHub Actions, we are permitted only 5 concurrent MacOS jobs. A single PR requires that many jobs, one in bokehjs-ci and four in bokeh-ci. I've been canceling old test runs for the past day, to make room for newer or more important PRs, and it's still very hard to get anything tested to completion.

I think we can reduce this to one job. bokehjs doesn't need to be tested on MacOS. If it works on Linux and Windows, it's improbable it will fail on MacOS (we are testing on Chromium everywhere). In bokeh-ci, we can only run unit tests for the oldest supported Python (3.8 currently), and then test all supported versions in release-build workflow (and perhaps have more frequent dev releases).



user: mattpap
issue_12845: state: open
title: [BUG] Hover optiones for renderers apply on all renderers if HoverTool is activated
body: ### Software versions

Python version      :  3.9.10 | packaged by conda-forge | (main, Feb  1 2022, 21:24:11) 
IPython version     :  8.1.1
Tornado version     :  6.1
Bokeh version       :  3.0.3
BokehJS static path :  /opt/conda/envs/bk3/lib/python3.9/site-packages/bokeh/server/static
node.js version     :  v17.4.0
npm version         :  8.3.1
Operating system    :  Linux-4.19.0-21-amd64-x86_64-with-glibc2.31

### Browser name and version

Google Chrome Version 110.0.5481.178 (Build) (64-Bit)

### Jupyter notebook / Jupyter Lab version

Selected Jupyter core packages... IPython          : 8.1.1 ipykernel        : 6.9.1 ipywidgets       : 7.6.5 jupyter_client   : 7.1.2 jupyter_core     : 4.9.2 jupyter_server   : not installed jupyterlab       : not installed nbclient         : 0.5.12 nbconvert        : 6.4.2 nbformat         : 5.1.3 notebook         : 6.4.8 qtconsole        : not installed traitlets        : 5.1.1

### Expected behavior

It is expected that I can define the appearance on hover events for renderers. The renderers are connected to an Hovertool and the optical change should only appear if the HoverTool is active. If multiple HoverTools are defined, only renderers with an relation to this HoverTool and not all renderers should be effected.

### Observed behavior

If I define one HoverTool with one renderer, and other renderers are also existing, all rendereres change how they look.
If I have multiple HoverTools, one HoverTool does not react and on the second som renderers diesappear.

###  Example code and Screenshots

#### one HoverTool

```python
from bokeh.plotting import figure, show, output_notebook
from bokeh.models import ColumnDataSource, HoverTool
output_notebook()

source = ColumnDataSource({'x':[1,2,3,4,5], 'y1': [1,2,3,4,5], 'y2': [2,3,4,5,6]})

p = figure(width=500, height=350)

l1 = p.line(x='x', y='y1', source=source, legend_label='y1', hover_line_width=5)

l2 = p.line(x='x', y='y2', source=source, color='green', legend_label='y2',
            hover_line_color='green', hover_line_width=4)

c2 = p.circle(x='x', y='y2', source=source, color='red', legend_label='C2',
              hover_line_color='red', hover_line_width=4)

p.add_tools(HoverTool(renderers=[l1],tooltips=None))
# p.add_tools(HoverTool(renderers=[l2, c2],tooltips=None))

show(p)
```
![one_active_hovertool](https://user-images.githubusercontent.com/68053396/221148925-e7840cdd-6b5b-4260-b85d-458801dd31f9.gif)


#### two HoverTools

```python
from bokeh.plotting import figure, show, output_notebook
from bokeh.models import ColumnDataSource, HoverTool
output_notebook()

source = ColumnDataSource({'x':[1,2,3,4,5], 'y1': [1,2,3,4,5], 'y2': [2,3,4,5,6]})

p = figure(width=500, height=350)

l1 = p.line(x='x', y='y1', source=source, legend_label='y1', hover_line_width=5)

l2 = p.line(x='x', y='y2', source=source, color='green', legend_label='y2',
            hover_line_color='green', hover_line_width=4)

c2 = p.circle(x='x', y='y2', source=source, color='red', legend_label='C2',
              hover_line_color='red', hover_line_width=4)

p.add_tools(HoverTool(renderers=[l1],tooltips=None))
p.add_tools(HoverTool(renderers=[l2, c2],tooltips=None))

show(p)
```

![two_active_hovertools](https://user-images.githubusercontent.com/68053396/221148921-96e98959-d7e9-4d90-8593-b65b8c8974d6.gif)

user: mosc9575
issue_12844: state: open
title: [FEATURE] Mathematical notation (Latex) in legend labels
body: ### Problem description

Would be nice to have mathematical notation (latex, math mode) in the legend labels. 
Current release '3.0.3' does not support it. 

Though it's great it's now possible to use math mode in axis as an academic I would really appreciate it on legend labels as well.



### Feature description

Mathematical notation in the legend labels. 

### Potential alternatives

Not sure if any alternative possible.

### Additional information

_No response_
user: albe-jj
labels: tag: MathText: Related to MathText (TeX/MathML/AsciiMath)
issue_12835: state: open
title: Allow to retrieve models with `Bokeh.Models.ModelName`
body: Models are available via `Bokeh.ModelName` if `bokeh-api` is present. This was done to limit the API surface for `Bokeh` global to the minimum by default. This is also why when `bokeh-widgets` is included, widgets appear under `Bokeh.Widgets` namespace. It's possible to retrieve any model with `Bokeh.Models.get("ModelName")`, but that's a bit cumbersome, so I added support for `Bokeh.Models.ModelName`, using a proxy. Proxies are generally slow, but this doesn't affect performance, because this doesn't affect the `default_resolver`, which is used in deserialization code. I will update documentation to point users to `Bokeh.Models`, which hopefully will allow us to stop polluting `Bokeh` namespace at some point in the future.

fixes #12638

user: mattpap
issue_12824: state: open
title: [BUG] ImageURL does not appear in legends.
body: ### Software versions

Python version      :  3.10.9 (main, Jan 11 2023, 09:18:20) [Clang 14.0.6 ]
IPython version     :  8.10.0
Tornado version     :  6.2
Bokeh version       :  3.0.3
BokehJS static path :  /Users/ekruse/anaconda/envs/py310/lib/python3.10/site-packages/bokeh/server/static
node.js version     :  (not installed)
npm version         :  (not installed)
Operating system    :  macOS-10.16-x86_64-i386-64bit


### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

I'm not entirely sure if this is a bug or a feature request, but I'd like some clarification or additional documentation about this. I'd expect that the `ImageURL` image would appear in some form in the legends, since it seems to function identically to other things you can put in legends.

### Observed behavior

You can get the legend label in there, and you can even toggle it visible or not by clicking either on that label or the empty space where the icon is supposed to show up. It's just that nothing is there.

### Example code

```Python
import numpy as np

from bokeh.io import curdoc, show
from bokeh.models import ColumnDataSource, Grid, ImageURL, LinearAxis, Plot, Range1d, Legend, Line

url = "https://static.bokeh.org/logos/logo.png"
N = 5

source = ColumnDataSource(dict(
    url = [url]*5,
    x1  = np.linspace(  0, 150, N),
    y1  = np.linspace(  0, 150, N),
    w1  = np.linspace( 10,  50, N),
    h1  = np.linspace( 10,  50, N),
    x2  = np.linspace(-50, 150, N),
    y2  = np.linspace(  0, 200, N),
))

xdr = Range1d(start=-100, end=200)
ydr = Range1d(start=-100, end=200)

plot = Plot(
    title=None, x_range=xdr, y_range=ydr, width=500, height=500,
    min_border=0, toolbar_location=None)

x = np.linspace(-100, 200, 400)
y = np.linspace(-100, 200, 400)

source1 = ColumnDataSource(data=dict(x=x, y=y))
line_glyph = Line(x="x", y="y", line_color="navy", line_width=2, line_dash="dashed")
line = plot.add_glyph(source1, line_glyph)

image1 = ImageURL(url="url", x="x1", y="y1", w="w1", h="h1", anchor="center")
glyph1 = plot.add_glyph(source, image1)

image2 = ImageURL(url="url", x="x2", y="y2", w=20, h=20, anchor="top_left")
glyph2 = plot.add_glyph(source, image2)

xaxis = LinearAxis()
plot.add_layout(xaxis, 'below')

yaxis = LinearAxis()
plot.add_layout(yaxis,'left')

plot.add_layout(Grid(dimension=0, ticker=xaxis.ticker))
plot.add_layout(Grid(dimension=1, ticker=yaxis.ticker))

legend = Legend(items=[("line", [line]), ("image 1", [glyph1]),
                       ("image 2", [glyph2])], location="top")
plot.add_layout(legend, 'above')
plot.legend.click_policy="hide"

curdoc().add_root(plot)

show(plot)
```


### Stack traceback or browser console output

_No response_

### Screenshots

_No response_
user: ethankruse
issue_12815: state: open
title: Fix Either to run transform on the first param that pass validation
body: See https://github.com/bokeh/bokeh/pull/12775#issuecomment-1435162651
Basically to fix this I had to do the following:
Create a list that keeps which values pass validation and run transform on the first param that pass the validation, but there are some edge cases:
- if all params fail validation, just run transform 1-by-1
- validation can be disabled by the user, in the case again just run transform 1-by-1
- sometimes when all params fail validation, convertors are executed here: https://github.com/bokeh/bokeh/blob/226fb6aff189458dd5e5616c8fc375494b05ec2b/src/bokeh/core/property/bases.py#L356, but this is via is_valid which does not update the valids list in Either, so once we find a valid converter and value, we need to re-run the validation function to update the valids list.

- [x] issues: fixes #11830 


user: orena1
issue_12802: state: open
title: [FEATURE] Hover tooltip on arbitrary widgets
body: ### Problem description

I was trying to use the new feature of adding tooltips to [arbitrary UI elements](https://docs.bokeh.org/en/latest/docs/user_guide/interaction/tooltips.html#adding-tooltips-to-arbitrary-ui-elements).

Minimal example:

```python
from bokeh.document import Document
from bokeh.embed import file_html
from bokeh.models import Button, Tooltip, CustomJS
from bokeh.resources import INLINE
from bokeh.util.browser import view
from bokeh.events import ButtonClick

button = Button(label='Hello world')
tooltip = Tooltip(content='My Tooltip', position='right', visible=True, target=button, name='tooltip')

button.js_on_event(ButtonClick,
                   CustomJS(code="Bokeh.documents[0].get_model_by_name('tooltip').visible = "
                                 "!Bokeh.documents[0].get_model_by_name('tooltip').visible"))

doc = Document()
doc.add_root(button)
doc.add_root(tooltip)

with open('test.html', 'w') as f:
    f.write(file_html(doc, INLINE, 'Hello world'))
view('test.html')
```

However, the tooltip is persistent, i.e. it is not a tooltip that only pops up when hovering over the UI element. I didn't find a way to make this work.

### Feature description

It would be nice having the option to make the tooltip appear only when hovering over the UI element, perhaps with a dedicated attribute in the `Tooltip` class.

### Potential alternatives

An alternative solution would be if UI elements such as the `Button` in the minimal example accepted `MouseEnter`/`MouseLeave` events. In this case one could make the tooltip appear/disappear with a `CustomJS` callback as shown with the `ButtonClick` event in the minimal example.

### Additional information

_No response_
user: m-guggenmos
issue_12781: state: open
title: Use real events for patching and streaming
body: ### Software versions

N/A

### Browser name and version

N/A

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

Pretty minor but cost me some time figuring out today. Specifically under https://docs.bokeh.org/en/latest/docs/user_guide/interaction/callbacks.html#customjs-for-model-property-events :

> ... For example, the ColumnDataSource also supports "patch" and "stream" events, for executing CustomJS callbacks whenever the data source is patched or streamed to.

First of all, it's kinda misleading to use the word "events" when the setup is to actually use js_on_change, NOT js_on_event.

Secondly, it's NOT

`js_on_change("patch"/"stream", CustomJS(...))`

it is actually

`js_on_change("streaming"/"patching",CustomJS(...))`

I trawled through a number of issues/discourse posts and have seen the `js_on_change('stream',...)` several times, not sure if this has changed historically or it's just been overlooked.

### Observed behavior

N/A

### Example code

```Python
N/A
```


### Stack traceback or browser console output

_No response_

### Screenshots

_No response_
user: gmerritt123
issue_12780: state: open
title: Draw/edit tools inconsistent with CDS stream/patch [BUG] 
body: ### Software versions

Python version      :  3.8.15 | packaged by conda-forge | (default, Nov 22 2022, 08:42:03) [MSC v.1929 64 bit (AMD64)]
IPython version     :  8.7.0
Tornado version     :  6.2
Bokeh version       :  3.0.3
BokehJS static path :  C:\ProgramData\Anaconda3\envs\geo_env38v1\lib\site-packages\bokeh\server\static
node.js version     :  v18.12.1
npm version         :  8.19.2
Operating system    :  Windows-10-10.0.19044-SP0

### Browser name and version

Chrome: Version 96.0.4664.45 (Official Build) (64-bit

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

First of all, this may actually be miscategorized by me (could also be construed as a feature request).

I can trigger CustomJS on CDS stream/patch events using "source.js_on_change('streaming', CustomJS).

However, around line 40ish here in the source code (polydraw tool for example) :https://github.com/bokeh/bokeh/blob/branch-3.1/bokehjs/src/lib/models/tools/edit/poly_draw_tool.ts , it appears that stream/patch methods AREN'T used to push or edit new data on the CDS when a new line is drawn or an existing one is edited. This means I can't trigger CustomJS on a "draw event".

### Observed behavior

My MRE below has two callbacks, one to listen for a stream event, and another one to execute a stream operation on button click (this part works). To demonstrate what doesn't, there is also a polydraw tool attached to the figure, and using it to draw a new line DOES NOT trigger the stream event.

If there's another way to trigger a "user draw/edit" event, I'm all ears to it --> I just thought this was by far the most intuitive way to go about setting it up and ran into this.

Thanks as always! 

### Example code

```Python
from bokeh.plotting import figure, show
from bokeh.layouts import column
from bokeh.models import CustomJS, PolyDrawTool, ColumnDataSource, Button

src = ColumnDataSource(data={'xs':[],'ys':[],'id':[]})

f = figure(x_range=[0,10],y_range=[0,10])
r = f.multi_line(xs='xs',ys='ys',source=src,line_color='black')

pt = PolyDrawTool(renderers=[r],empty_value='')

cb = CustomJS(code='''console.log('stream event!)''')

f.add_tools(pt)

src.js_on_change('streaming',cb)

btn = Button(label='stream something')
cb2 = CustomJS(args=dict(src=src)
               ,code='''
               var nverts = Math.floor(Math.random()*5)+2
               console.log(nverts)
               var new_line = {'xs':[[]],'ys':[[]],'id':''}
               for (var i=0; i < nverts; i++){
                       console.log(i)
                       new_line['xs'][0].push(Math.random()*10)
                       new_line['ys'][0].push(Math.random()*10)
                       }
               
               src.stream(new_line)
               console.log(src.data)
               '''
               )
btn.js_on_click(cb2)

show(column([f,btn]))
```


### Stack traceback or browser console output

_No response_

### Screenshots

_No response_
user: gmerritt123
issue_12777: state: open
title: [FEATURE] Support for hit-testing events in Label/HTMLLabel
body: ### Problem description

As described in a [previous Discourse conversion](https://discourse.bokeh.org/t/trigger-javascript-callback-when-clicking-on-htmllabel-in-figure/9976), I am trying to invoke some javascript code when the user clicks on a `HTMLLabel`. However, as pointed out by @bryevdv , the `HTMLLabel` model does not support hit-testing events at this point.

In my particular use case, I need the label position to be defined in screen coordinates (minimal example):
```python
from bokeh.plotting import figure, show
from bokeh.models import HTMLLabel
x = [1, 2, 3, 4, 5]
y = [6, 7, 2, 4, 5]
p = figure(title="Simple line example", x_axis_label="x", y_axis_label="y")
p.line(x, y, legend_label="Temp.", line_width=2)
label = HTMLLabel(x=10, y=10, x_units='screen', y_units='screen', text='Hello World', border_line_color='#444')
p.add_layout(label)
show(p)
```


### Feature description

It would be nice if `Label`/`HTMLLabel` could support hit-testing events.

### Potential alternatives

I was trying to make this work by inferring the mouse position in the plot area and triggering code when the mouse is above the label area. However, this turned out to be very difficult when the label is defined in screen coordinates, because I would need to know the location of the axis area within the overall frame area. Specifically, while the javascript level exposes `inner_height` and `inner_width` of the axis area, it does not expose something like `inner_x_position` and `inner_y_position` (in screen space), which would be required for this solution.

### Additional information

_No response_
user: m-guggenmos
issue_12764: state: open
title: Colorbars need directional padding or margins
body: Currently, it is not possible to have, e.g. a vertical colorbar that extends the full length of the y-axis, but that has some horizontal separation from the axis. Perhaps `padding` and/or `margin` could also accept `Tuple(Int, Int)` etc. for finer grained directional values.  

cc @mattpap please correct me if that is wrong, I did try several things to try and achieve this. 
user: bryevdv
issue_12759: state: open
title: [FEATURE] Support using CSS Variables in place of colors
body: ### Problem description

Since Bokeh 3.0 using the `stylesheets` properties on models makes it quite straightforward achieving consistent styling and we can finally use CSS variables to provide orderly style cascades. However there is now a disconnect between things that can be styled via CSS properties and things that are styled directly via Bokeh properties. 

### Feature description

It would therefore be nice to allow any bokeh `Color` property to directly accept a CSS variable. From an implementation perspective I suspect this may not easy. This is because many color properties actually get applied via the `CanvasRenderingContext2D` and therefore are not directly linked to the variable. This means it probably requires watching for changes to the variables and redrawing the canvas. However I think this is the only way we will ever achieve consistent theming across the DOM and the canvas elements so I think it is at least worth an investigation.

### Potential alternatives

An alternative could be for themes to be applied client side and resolve variables before they are applied. This wouldn't be responsive to changes in variables but at least would allow defining colors in a single place.

### Additional information

_No response_
user: philippjfr
issue_12758: state: open
title: Programattic tool activation differs from interactive
body: Example:
```py
from bokeh.models import BoxEditTool, ColumnDataSource, Button
from bokeh.plotting import figure, curdoc #show
from bokeh.layouts import column
from bokeh.transform import dodge

p = figure(x_range=(0, 10), y_range=(0, 10), width=400, height=400,
           title='Box Edit Tool')

src = ColumnDataSource({
    'x': [5, 2, 8], 'y': [5, 7, 8], 'width': [2, 1, 2],
    'height': [2, 1, 1.5], 'alpha': [0.5, 0.5, 0.5]
})

r0 = p.rect('x', 'y', 'width', 'height', source=src, alpha='alpha', color="red")
r1 = p.rect(dodge('x', 1), dodge('y', 1), 'width', 'height', source=src._clone(), alpha='alpha', color="blue")

draw_tool_0 = BoxEditTool(renderers=[r0], empty_value=1)
draw_tool_1 = BoxEditTool(renderers=[r1], empty_value=1)
p.add_tools(draw_tool_0, draw_tool_1)
p.toolbar.active_drag = draw_tool_0

b = Button(label="Change tool")
def click():
    if p.toolbar.active_drag == draw_tool_0:
        p.toolbar.active_drag = draw_tool_1
    else:
        p.toolbar.active_drag = draw_tool_0
b.on_click(click)

curdoc().add_root(column([p, b]))
```
https://user-images.githubusercontent.com/27475/215117124-ddc30874-6f66-4660-a684-7f09b30b9d5f.mp4
user: mattpap
issue_12747: state: open
title: Don't specify default value for `FactorRange.{start,end}`
body: Prior to PR #12565 all readonly properties had an intrinsic default value, which usually doesn't make sense. So now they don't except for this, because `FactorRange` in bokehjs sets `start` and `end` during model initialization, which always results in 0, because that happens before sensible values can be computed. Due to how defaults' testing works, this requires setting the default value in bokeh. Ultimately this default will be removed, which will require changes to `FactorRange`'s initialization, which I don't want to get into here.

_Originally posted by @mattpap in https://github.com/bokeh/bokeh/pull/12565#discussion_r1018942896_
            
user: mattpap
issue_12738: state: open
title: [BUG] Bad error message when firefox fails to start
body: ### Software versions

Python version      :  3.7.12 | packaged by conda-forge | (default, Oct 26 2021, 06:08:53)
IPython version     :  (not installed)
Tornado version     :  6.2
Bokeh version       :  2.4.3
BokehJS static path :  /opt/conda/envs/ukb/lib/python3.7/site-packages/bokeh/server/static
node.js version     :  (not installed)
npm version         :  (not installed)
Operating system    :  Linux-5.10.102.1-microsoft-standard-WSL2-x86_64-with-debian-11.6

### Browser name and version

```
firefox                   108.0.2              hcb278e6_0    conda-forge
geckodriver               0.32.0               h4b87306_0    conda-forge
selenium                  4.7.2              pyhd8ed1ab_0    conda-forge
```

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

bokeh.io.export_png to succeed

### Observed behavior

```
Traceback (most recent call last):
  File "/cromwell-executions/main/4ebbd285-2b3a-4794-b1bc-51f7fabcf33e/call-main/main/4d38da92-20e0-42ec-bc58-cab21a23c8b4/call-fig_4a/inputs/1414132764/cbl_allele_histo.py", line 130, in <module>
    bokeh.io.export_png(fig, filename=f'{args.outdir}/cbl.png')
  File "/opt/conda/envs/ukb/lib/python3.7/site-packages/bokeh/io/export.py", line 111, in export_png
    image = get_screenshot_as_png(obj, width=width, height=height, driver=webdriver, timeout=timeout)
  File "/opt/conda/envs/ukb/lib/python3.7/site-packages/bokeh/io/export.py", line 236, in get_screenshot_as_png
    web_driver = driver if driver is not None else webdriver_control.get()
  File "/opt/conda/envs/ukb/lib/python3.7/site-packages/bokeh/io/webdriver.py", line 153, in get 
    self.current = self.create()
  File "/opt/conda/envs/ukb/lib/python3.7/site-packages/bokeh/io/webdriver.py", line 157, in create
    driver = self._create(kind)
  File "/opt/conda/envs/ukb/lib/python3.7/site-packages/bokeh/io/webdriver.py", line 175, in _create
    raise RuntimeError("Neither firefox and geckodriver nor a variant of chromium browser and " \ 
RuntimeError: Neither firefox and geckodriver nor a variant of chromium browser and chromedriver are available on system PATH. You can install the former with 'conda install -c conda-forge firefox geckodriver'.
```

### Example code

It turns out that despite having `firefox` and `geckodriver` installed via conda and accessible on the PATH, `firefox` was still missing some dependencies. I confirmed this via:

```
> firefox --headless
XPCOMGlueLoad error for file /opt/conda/envs/ukb/bin/FirefoxApp/libmozgtk.so:
libgtk-3.so.0: cannot open shared object file: No such file or directory
Couldn't load XPCOM.
```

and fixed it via `apt-get install -y libgtk-3-0 libasound2 libdbus-glib-1-2 libx11-xcb-dev libpci-dev libgl1-mesa-glx`

So it's not Bokeh's fault that `firefox` wasn't working or that export_png failed. But Bokeh swallows all error messages [here](https://github.com/bokeh/bokeh/blob/e8c895f15d2f9ad456e1caf71c16c0f5de8d011b/bokeh/io/webdriver.py#L115) and so instead of reporting that my firefox installation was broken with the appropriate error message, it gave an incorrect error message suggesting that firefox wasn't installed at all. This led me down multiple hours of incorrect debugging. And I've seen many similar message board posts revolving around this issue. Instead of swallowing that error - can bokeh surface it so the user knows how to debug this?


### Stack traceback or browser console output

_No response_

### Screenshots

_No response_
user: LiterallyUniqueLogin
issue_12737: state: open
title: Don't calculate contours that aren't displayed
body: Contour lines and filled contours have vectorised visual properties. Some of these properties may result in lines/polygons not being displayed such as when `alpha=0`. The current contour implementation calculates the lines/polygons anyway, regardless of whether they will be displayed or not. We should not calculate those that are not displayed.

An example use case is displaying a complete set of filled contours for the surface temperature of the earth but the only line displayed is the one at a temperature of 0°C.

Self-assigning.
user: ianthomas23
labels: python: Issues that should only require updating Python code
issue_12728: state: open
title: [BUG] Size mismatch error when updating ColumnDataSource data ONLY when using tabs and showing the same figure in both tabs
body: ### Software versions

Python version      :  3.9.5 (default, Jun  4 2021, 12:28:51)
IPython version     :  (not installed)
Tornado version     :  6.1
Bokeh version       :  3.0.3
BokehJS static path :  /home/lele/.conda/envs/py3.9-torch1.9-opencv/lib/python3.9/site-packages/bokeh/server/static
node.js version     :  v16.19.0
npm version         :  8.19.2
Operating system    :  Linux-6.1.1-arch1-1-x86_64-with-glibc2.36

### Browser name and version

Google Chrome Version 108.0.5359.124 (Official Build) (64-bit)

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

The attached code should run without errors.

### Observed behavior

When updating source.data in a python callback (see code), the browser console throw a `Uncaught Error: Size mismatch`.
The code works perfectly if the `Tabs` are not used, or if in the two tabs two different figure are shown.

Code is commented with these options, and throw the exception when the value in the selector is changed.

Am I doing something wrong and this is expected?

### Example code

```Python
from bokeh.io import curdoc
from bokeh.layouts import column, row
from bokeh.models import ColumnDataSource, TabPanel, Tabs, Select
from bokeh.plotting import figure

source = ColumnDataSource(dict(x=[1], y=[1], color=['red']))
p = figure(height=300, width=300)
p.circle(x='x', y='y', color='color', source=source)


def update(attr, old, new):
    source.data = dict(x=[1, 2], y=[1, 2], color=['red', 'blue'])
    source.data = dict(x=[1], y=[1], color=['red'])


select = Select(title=f'select', value='a', options=['a', 'b'])
select.on_change('value', update)

tab0 = TabPanel(child=row(p, select), title="A")
tab1 = TabPanel(child=column(p, select), title="B")  # code works if here a different p is shown

curdoc().add_root(column(Tabs(tabs=[tab0, tab1])))  # code does not work with this
# curdoc().add_root(column(Tabs(tabs=[tab0])))  # code does work with this line instead (same using tab1)
```


### Stack traceback or browser console output

_No response_

### Screenshots

_No response_
user: santelelle
issue_12713: state: open
title: [BUG] Seemingly random deadlocks with multiprocessing pools
body: ### Software versions

I first noticed the issue with previous versions of bokeh and python but it is still there with bokeh 3.0.3. It occurs on all Linux distributions I have tried so far (Ubuntu 18.04 and 22.04, Fedora 36, RHEL7).

```
Python version      :  3.11.0 | packaged by conda-forge | (main, Oct 25 2022, 06:24:40) [GCC 10.4.0]
IPython version     :  (not installed)
Tornado version     :  6.2
Bokeh version       :  3.0.3
BokehJS static path :  /home/mathurin/miniconda3/envs/bokeh3/lib/python3.11/site-packages/bokeh/server/static
node.js version     :  (not installed)
npm version         :  (not installed)
Operating system    :  Linux-5.15.0-56-generic-x86_64-with-glibc2.35
```

### Expected behavior

I have an app which uses multiprocessing. I would expect that parallel tasks can be carried out seamlessly within a bokeh server process.

### Observed behavior

I noticed about a year ago that for no obvious reason, multiprocessing pools created within a bokeh server sometimes happen to be stuck. To circumvent that I tried to set up a mechanism to kill the pool if it hit a timeout. However it does not work, while it does work in a plain python script (see issue #12452).

When investigating the original issue I found that even a pool with a trivial task (no network I/O, no disk access, no heavy computation, no nothing) can result in a deadlock in a seemingly random fashion.

### Example code

Once triggered, the callback in the app below will indefinitely perform a very simple task. In my case, if I let it run for a few minutes I will inevitably get a deadlock, although I cannot predict when (can be a few tens as well as a few thousands of iterations).

As a control I tried several times to execute the callback in a python script and I got no hanging past 20k iterations. 

```Python
import multiprocessing as mp
import time
from bokeh.io import curdoc
from bokeh.models import Button


def task(num):
    return 2*num


def callback(new=None):

    a = 0
    while True:
        a += 1
        with mp.Pool(4) as p:
            res = p.map(task, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
        print(f'Finished another set of tasks (#{a})')
        time.sleep(0.1)


button = Button()
button.on_click(callback)

curdoc().add_root(button)
```

Another difference with multiprocessing within a bokeh server is the presence of lines that read "Received signal 15, shutting down" in the logs.

### Stack traceback or browser console output

Below is the traceback when I stop the deadlock process manually. Locks are involved.

```
Traceback (most recent call last):
  File "/home/mathurin/miniconda3/envs/bokeh3/lib/python3.11/multiprocessing/process.py", line 314, in _bootstrap
    self.run()
  File "/home/mathurin/miniconda3/envs/bokeh3/lib/python3.11/multiprocessing/process.py", line 108, in run
    self._target(*self._args, **self._kwargs)
  File "/home/mathurin/miniconda3/envs/bokeh3/lib/python3.11/multiprocessing/pool.py", line 114, in worker
    task = get()
           ^^^^^
  File "/home/mathurin/miniconda3/envs/bokeh3/lib/python3.11/multiprocessing/queues.py", line 364, in get
    with self._rlock:
  File "/home/mathurin/miniconda3/envs/bokeh3/lib/python3.11/multiprocessing/synchronize.py", line 95, in __enter__
    return self._semlock.__enter__()
           ^^^^^^^^^^^^^^^^^^^^^^^^^
```
user: TheoMathurin
labels: python: Issues that should only require updating Python code
issue_12707: state: open
title: [FEATURE] Inheritance diagrams in docs
body: ### Problem description

I think it would be helpful to include class inheritance diagrams in the sphinx docs. The API reference understandably contains a lot of text, I would like to break this up a bit with inheritance diagrams and I think it would help people who are visual learners (i.e. most bokeh users and contributors) to better understand the class structure.

### Feature description

Inheritance diagrams in appropriate places in the docs. Particularly useful, so maybe a good starting place, would be `bokeh.models`.

### Potential alternatives

Do nothing.

### Additional information

There is a sphinx extension for this (https://www.sphinx-doc.org/en/master/usage/extensions/inheritance.html) but that requires `graphviz` to generate the diagrams. I understand there used to be some in bokeh but these were removed in 2016 (https://github.com/bokeh/bokeh/pull/4825).

I think it would be better for us to write our own sphinx extension to generate the diagrams, using our `GraphRenderer`, rather than require `graphviz`. The proposed new circumscribe glyphs, e.g. ellipse around contained text, would be useful here.
user: ianthomas23
issue_12706: state: open
title: [BUG] Dynamic update of HoverTool tooltips does not update tooltip 
body: ### Software versions

Python version      :  3.8.13 | packaged by conda-forge | (default, Mar 25 2022, 05:59:45) [MSC v.1929 64 bit (AMD64)]
IPython version     :  7.33.0
Tornado version     :  6.2
Bokeh version       :  3.0.0.dev20
BokehJS static path :  C:\ProgramData\Anaconda3\envs\geo_env38v1\lib\site-packages\bokeh\server\static
node.js version     :  (not installed)
npm version         :  (not installed)
Operating system    :  Windows-10-10.0.19044-SP0

### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

Changing the tooltips on a HoverTool via CustomJS  does not result in the tooltip actually being updated in the plot.

Identified here: https://discourse.bokeh.org/t/update-tooltips-with-customjs-callback-function/9907/2, 



### Observed behavior

Looks like the tooltips property successfully updates according the console.log() statements, but the plot tooltip does not correspond.

### Example code

```Python
from bokeh.plotting import figure, show
from bokeh.layouts import column
from bokeh.models import CustomJS, HoverTool, Select

f = figure()
f.scatter(x=[1,2,3],y=[1,2,3],size=5)

hvr=HoverTool(tooltips=[('A','$x')])

f.add_tools(hvr)

sel = Select(options=['A','B'],value='A')
cb = CustomJS(args=dict(hvr=hvr,sel=sel)
              ,code='''
              console.log(hvr)
              console.log(hvr.tooltips[0][0])
              hvr.tooltips[0][0]=sel.value
              console.log(hvr.tooltips[0][0])
              hvr.change.emit()              
              ''')
sel.js_on_change('value',cb)
show(column([f,sel]))
```


### Stack traceback or browser console output

_No response_

### Screenshots

_No response_
user: gmerritt123
issue_12691: state: open
title: [BUG] text_outline_color should stroke before fill
body: ### Software versions

Python version      :  3.9.9 (main, Jan  1 1970, 00:00:01) 
IPython version     :  8.2.0
Tornado version     :  5.1.1
Bokeh version       :  3.0.3
BokehJS static path :  /home/user/.local/lib/python3.9/site-packages/bokeh/server/static
node.js version     :  (not installed)
npm version         :  (not installed)
Operating system    :  Linux-6.0.10-x86_64-with-glibc2.33

### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

Outlining text should not render it unreadable under default, and most reasonable, conditions.

### Observed behavior

The fix to issue https://github.com/bokeh/bokeh/issues/12179 applies fill before stroke, making text unreadable even for default font sizes. The test provided overlooked this because it uses a large font size.

### Example code

```Python
import bokeh
cds = bokeh.models.ColumnDataSource(dict(rtext=['Red text over black background should be readable regardless of its outline.'], stext=['1em text should be readable too!']))
fig = bokeh.plotting.figure(background_fill_color="red", x_range=(-2, 2), y_range=(-2, 2), width=1000)
fig.text(0, 1, text='rtext', text_color='blue', text_outline_color='red', text_align='center', source=cds)
fig.text(0, -1, text='stext', text_font_size='1em', text_color='white', text_outline_color='blue', text_align='center', source=cds)
bokeh.plotting.output_file('test_text_outline_color.html', mode='inline')
bokeh.plotting.save(fig)
```


### Stack traceback or browser console output

The patch should be trivial so I won't bother offering a pull request, but if it helps motivate you folks let me know.

### Screenshots

This is what it looks like **today**:
![Screenshot from 2022-12-21 04-18-00](https://user-images.githubusercontent.com/27959/208812677-9d7c5ed5-8201-4e3c-a18b-80f410eb0f32.png)


This is what you get **with stroke before fill**:
![Screenshot from 2022-12-21 04-17-46](https://user-images.githubusercontent.com/27959/208812747-4a859844-0488-48b9-a5db-697a45881cca.png)
user: solstag
issue_12682: state: open
title: Release actions should surface more information
body: Currently our release actions pipeline only surfaces "internal" log details in case there is an error, but sometimes it is desirable to access a log even when there was success. Additionally, it is not currently knowable how long each step in the pipeline took to perform. Propose:
 
* Upload full raw pipeline log as a GH action artifact
* Report timestamp/duration for each completed step in the pipeline
user: bryevdv
labels: python: Issues that should only require updating Python code
issue_12671: state: open
title: Pixel-snapping option for axis-aligned glyphs
body: Axis-aligned glyphs such as `quad`, `rect`, `vbar`, and `hbar` (i.e. all the varieties of `lrtb`) should have an option to snap screen coordinates to integers so that the rect borders are pixel sharp, without any "aliasing" along the edges. This request comes out of some discussions with a potential user of BokehJS. 

cc @ianthomas23 who has expressed some initial thoughts and interest. 
user: bryevdv
issue_12665: state: open
title: [BUG] Incorrect legend using same legend_field option from several source with different label set
body: ### Software versions

Python version      :  3.10.9 (tags/v3.10.9:1dd9be6, Dec  6 2022, 20:01:21) [MSC v.1934 64 bit (AMD64)]
IPython version     :  (not installed)
Tornado version     :  6.2
Bokeh version       :  2.4.3
BokehJS static path :  C:\Appl\env\buildup_auto_qc_310_TEST\lib\site-packages\bokeh-2.4.3-py3.10.egg\bokeh\server\static
node.js version     :  (not installed)
npm version         :  (not installed)
Operating system    :  Windows-10-10.0.19045-SP0

### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

Having a figure with 2 quad dataset plot on top of each other. They use same legend-coloring schema created using the legend_field option.

In figure 1 the 2 sources have all same 3 labels used, the output legend is correct.
In figure 2 and 3, the 2 sources have different number of labels used and the order the 2 datasets are plotted is inverted.
Expectation is that legend should be as in figure 1.


### Observed behavior

In figure 2 with source having less label used plotted second, the output legend contains the 3 labels however the one missing in the second source has incorrect colour.

In figure 3 with source having less label used plotted first, the output legend contains only the 2 labels from that source.

Note that the plotting colouring is always correct

### Example code

```Python
import panel as pn
import bokeh
import pandas as pd

from bokeh.models import ColumnDataSource
from bokeh.plotting import show

pn.extension()


def create_data_source(lefts, rights, labels):
    def label_colour(label):
        if label == "A":
            return "red"
        elif label == "B":
            return "green"
        elif label == "C":
            return "blue"
        else:
            return "gray"

    colours = [label_colour(label) for label in labels]
    df = pd.DataFrame(
        {"left": lefts, "right": rights, "label": labels, "colour": colours}
    )

    return ColumnDataSource(
        data=dict(left=df.left, right=df.right, label=df.label, colour=df.colour)
    )


def create_ds_1_source():
    left = [0, 4, 7, 12, 34, 65, 80]
    right = [4, 7, 12, 34, 65, 80, 100]
    label = [
        "A",
        "B",
        "C",
        "A",
        "C",
        "B",
        "C",
    ]
    return create_data_source(left, right, label)


def create_ds_2_source():
    left = [0, 6, 25, 44, 56, 78, 90]
    right = [6, 25, 44, 56, 78, 90, 100]
    label = [
        "A",
        "B",
        "C",
        "A",
        "C",
        "B",
        "C",
    ]
    return create_data_source(left, right, label)


def create_ds_3_source():
    left = [0, 50]
    right = [50, 100]
    label = ["A", "C"]
    return create_data_source(left, right, label)


ds_1_source = create_ds_1_source()
ds_1_source_copy = create_ds_1_source()
ds_1_source_copy_copy = create_ds_1_source()
ds_2_source = create_ds_2_source()
ds_3_source = create_ds_3_source()
ds_3_source_copy = create_ds_3_source()

fig1 = bokeh.plotting.figure(
    title="FIG 1",
    sizing_mode="stretch_both",
)
fig1.add_layout(bokeh.models.Legend(), "left")
fig1.quad(
    source=ds_1_source,
    left="left",
    right="right",
    bottom=0,
    top=0.5,
    color="colour",
    legend_field="label",
)
fig1.quad(
    source=ds_2_source,
    left="left",
    right="right",
    bottom=0.5,
    top=1,
    color="colour",
    legend_field="label",
)
show(fig1)

fig2 = bokeh.plotting.figure(
    title="FIG 2",
    sizing_mode="stretch_both",
)
fig2.add_layout(bokeh.models.Legend(), "left")
fig2.quad(
    source=ds_1_source_copy,
    left="left",
    right="right",
    bottom=0,
    top=0.5,
    color="colour",
    legend_field="label",
)
fig2.quad(
    source=ds_3_source,
    left="left",
    right="right",
    bottom=0.5,
    top=1,
    color="colour",
    legend_field="label",
)
show(fig2)

fig3 = bokeh.plotting.figure(
    title="FIG 3",
    sizing_mode="stretch_both",
)
fig3.add_layout(bokeh.models.Legend(), "left")
fig3.quad(
    source=ds_3_source_copy,
    left="left",
    right="right",
    bottom=0,
    top=0.5,
    color="colour",
    legend_field="label",
)
fig3.quad(
    source=ds_1_source_copy_copy,
    left="left",
    right="right",
    bottom=0.5,
    top=1,
    color="colour",
    legend_field="label",
)
show(fig3)
```


### Stack traceback or browser console output

_No response_

### Screenshots

![image](https://user-images.githubusercontent.com/51163660/206444849-5584a659-aa24-48a3-9c37-93b73ec05116.png)

user: RaphaelRosset
issue_12660: state: open
title: [FEATURE] Dense Ticker
body: ### Problem description

When plotting on a big screen the ticks of a DatetimeTicker are really far apart. This makes it quite hard to read the coordinates just by looking at the plot. Even with crosshair tool it is quite hard to calculate where the lines are landing at. It would be nice to be able to make the Ticker simply have more ticks.

### Feature description

As discussed [here](https://discourse.bokeh.org/t/dense-datetimeticker/9788) on the example of the following code
```
from bokeh.plotting import figure, curdoc
from bokeh.models import ColumnDataSource, Plot, AdaptiveTicker, DaysTicker

import numpy as np
import pandas as pd

ONE_MILLI = 1.0
ONE_SECOND = 1000.0
ONE_MINUTE = 60.0 * ONE_SECOND
ONE_HOUR = 60 * ONE_MINUTE

TICKERS = [
    AdaptiveTicker(
        mantissas=[1],
        base=10,
        min_interval=ONE_HOUR,
        max_interval=2*ONE_HOUR,
        num_minor_ticks=0
    )
]


p = figure(width=300, height=300, tools="pan,reset,save,box_zoom,zoom_in,zoom_out", x_axis_type="datetime", sizing_mode="stretch_both")
p.xaxis.ticker.tickers = TICKERS

POINTS_COUNT = 100000
source = ColumnDataSource(data={"x": pd.date_range(start="2022-01-01", end="2022-01-03", periods=POINTS_COUNT), "y": np.random.rand(1, POINTS_COUNT)[0]})
doc = curdoc()

p.scatter(source=source)

doc.add_root(p)
```
all the modifications of the AdaptiveTicker are either unclear or ineffective. It would be nice to have either the algorithm for deciding ticks clearly explained somewhere or some dedicated parameter for density of the ticks.

### Potential alternatives

Solutions that come to my head are:
- Reproducible code examples that clearly show how changing specific parameters results in more ticks being shown. That would either include a working example of CompositeTicker with more than one AdaptiveTicker and a clear description of how the algorithm decides which Ticker to use or a working example using `desired_num_ticks`. 
- Being able to show Tick values attached to the minor ticks. For example in the case of DatetimeTicker on the x axis we could see Datetime under the minor ticks as well as under the major ticks. Then with `num_minor_ticks` we could easily specify the density of the ticks.

### Additional information

_No response_
user: geronimogoemon
issue_12658: state: open
title: [BUG] grid_line_alpha value of '1.0' doesn't work but '0.99' does work
body: ### Software versions

Python version      :  3.8.3 (default, Jul  2 2020, 17:30:36) [MSC v.1916 64 bit (AMD64)]
IPython version     :  8.1.1
Tornado version     :  6.1
Bokeh version       :  3.0.2
BokehJS static path :  .......\lib\site-packages\bokeh\server\static
node.js version     :  (not installed)
npm version         :  (not installed)
Operating system    :  Windows-10-10.0.19041-SP0

### Expected behavior

When using the `dark_minimal` theme, and setting the `grid_line_alpha` value to 1 (or 1.0):
I would expect the grid line to become opaque.

### Observed behavior

The grid line alpha does not change.

### Example code

```Python
from bokeh.plotting import curdoc, figure, show

curdoc().theme = "dark_minimal"
fig = figure()
fig.line([1, 2, 3, 4], [1, 2, 3, 4])
fig.title = "Before (grid_line_alpha=0.25 from theme)"
show(fig)

fig.grid.grid_line_alpha = 1.0
fig.title = "After (grid_line_alpha=1.0)"
show(fig)

fig.grid.grid_line_alpha = 0.99
fig.title = "After (grid_line_alpha=0.99)"
show(fig)
```

I expect to see the "After" plot having a more opaque grid line, than the "Before" graph.
However, they look the same.

But, if I instead set the grid_line_alpha to 0.99, then it visibly changes.

### Screenshots

![image](https://user-images.githubusercontent.com/97503920/205628992-aafee3cb-6f63-42d5-906c-302a2e45162d.png)

user: ribeirompl-soldersmith
labels: python: Issues that should only require updating Python code
issue_12638: state: open
title: Make Slider and Image available in BokehJS (JS only)
body: Hi,
As discussed with Bryan in https://discourse.bokeh.org/t/bokehjs-how-to-create-a-slider/9759/3, it would be interesting to have `Slider` and `Image` available directly in BokehJS.

The documentations states that nearly all Python models are available similarly in JS with `new Bokeh.<model>`. It seems that `new Bokeh.Slider(...)` and `new Bokeh.Image(...)` (I'm looking for [color mapped images](https://docs.bokeh.org/en/latest/docs/user_guide/topics/images.html#color-mapped-images)) are currently not available in JS-only BokehJS.
Do you think it would be straightforward to add it? I can do the beta-testing and reporting.

Thanks

PS: I used this issue template because the other ones were quite complex with many textboxes - this one is more classical.
user: josephernest
issue_12628: state: open
title: curdoc.clear() doesn't work anymore on 3.x
body: ### Software versions

Python version      :  3.9.10 (v3.9.10:f2f3f53782, Jan 13 2022, 16:55:45) 
IPython version     :  (not installed)
Tornado version     :  6.2
Bokeh version       :  2.4.3
BokehJS static path :  /Users/johannpayen/Dropbox/Dev Python/Dev Python NextOne/venv/lib/python3.9/site-packages/bokeh/server/static
node.js version     :  (not installed)
npm version         :  (not installed)
Operating system    :  macOS-12.6-arm64-arm-64bit


### Browser name and version

chrome

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

curdoc().clear() clear the document in Bokeh 2.x, since 3.x it stopped working.

There are other issues related : 
curdoc().title works only once
curdoc().add_root() works only once also

### Observed behavior

nothing seems to happen from the user point of view.

### Example code

```Python
import numpy as np

from bokeh.io import curdoc
from bokeh.layouts import column, row
from bokeh.models import ColumnDataSource, Slider, TextInput
from bokeh.plotting import figure

# Set up data
N = 200
x = np.linspace(0, 4*np.pi, N)
y = np.sin(x)
source = ColumnDataSource(data=dict(x=x, y=y))


# Set up plot
plot = figure(height=400, width=400, title="my sine wave",
              tools="crosshair,pan,reset,save,wheel_zoom",
              x_range=[0, 4*np.pi], y_range=[-2.5, 2.5])

plot.line('x', 'y', source=source, line_width=3, line_alpha=0.6)


# Set up widgets
text = TextInput(title="title", value='my sine wave')
offset = Slider(title="offset", value=0.0, start=-5.0, end=5.0, step=0.1)
amplitude = Slider(title="amplitude", value=1.0, start=-5.0, end=5.0, step=0.1)
phase = Slider(title="phase", value=0.0, start=0.0, end=2*np.pi)
freq = Slider(title="frequency", value=1.0, start=0.1, end=5.1, step=0.1)


# Set up callbacks
def update_title(attrname, old, new):
    plot.title.text = text.value
    if text.value =="clear":
        curdoc().clear()

text.on_change('value', update_title)

def update_data(attrname, old, new):

    # Get the current slider values
    a = amplitude.value
    b = offset.value
    w = phase.value
    k = freq.value

    # Generate the new curve
    x = np.linspace(0, 4*np.pi, N)
    y = a*np.sin(k*x + w) + b

    source.data = dict(x=x, y=y)

for w in [offset, amplitude, phase, freq]:
    w.on_change('value', update_data)


# Set up layouts and add to document
inputs = column(text, offset, amplitude, phase, freq)

curdoc().add_root(row(inputs, plot, width=800))
curdoc().title = "Sliders"
```


### Stack traceback or browser console output

_No response_

### Screenshots

_No response_
user: johann006
issue_12619: state: open
title: Automate issue or PR for point release forward ports
body: Need to bring release commit with updates to changelog, SRI hashes, etc. forward to the new branch. Making an issue so that I don't forget . 

Edit: submitted PR for 3.0.2, leaving this issue for adding an automated reminder or nudge of some sort.

I am a bit skeptical of trying to to full auto-commit these forward ports during the release, but perhaps we could automate issue or even PR generation during the release, to get us part-way there. 

Edit: Also need a reminder to reset `main` in case we don't fully automate that too. 
user: bryevdv
issue_12618: state: open
title: [BUG] ColorBar tick labels display inappropriate precision
body: ### Software versions

Python version      :  3.10.6 | packaged by conda-forge | (main, Aug 22 2022, 20:41:22) [Clang 13.0.1 ]
IPython version     :  8.5.0
Tornado version     :  6.2
Bokeh version       :  3.0.0+29.ga35573f0
BokehJS static path :  /Users/iant/github/bokeh/src/bokeh/server/static
node.js version     :  v18.11.0
npm version         :  8.19.2
Operating system    :  macOS-12.6.1-arm64-arm-64bit

### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

Better choice of numerical precision to display tick labels.

### Observed behavior

<img width="313" alt="Screenshot 2022-11-16 at 12 02 29" src="https://user-images.githubusercontent.com/580326/202177027-27f504dd-1e68-4c4b-95e9-3eb76d62f8e1.png">


### Example code

```Python
from bokeh.models import ColorBar, EqHistColorMapper
from bokeh.palettes import Blues9
from bokeh.plotting import figure, show
import numpy as np

im = np.array([[0.7, 0.7+1e-10]])

p = figure(width=300, height=300)

color_mapper = EqHistColorMapper(Blues9[::-1], rescale_discrete_levels=True)
p.image(image=[im], x=0, y=0, dw=10, dh=10, color_mapper=color_mapper)

color_bar = ColorBar(color_mapper=color_mapper)
p.add_layout(color_bar, 'right')

show(p)
```


### Stack traceback or browser console output

_No response_

### Screenshots

_No response_
user: ianthomas23
issue_12615: state: open
title: [BUG] `Figure.image` not working with "vanilla" Python data structures
body: ### Software versions

Python version      :  3.10.8 (tags/v3.10.8:aaaf517, Oct 11 2022, 16:50:30) [MSC v.1933 64 bit (AMD64)]
IPython version     :  (not installed)
Tornado version     :  6.2
Bokeh version       :  3.0.2
BokehJS static path :  C:\Users\markgras\Desktop\venvs\py310\lib\site-packages\bokeh\server\static
node.js version     :  (not installed)
npm version         :  (not installed)
Operating system    :  Windows-10-10.0.19045-SP0

### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

I expect [Figure.image](https://docs.bokeh.org/en/latest/docs/reference/plotting/figure.html#bokeh.plotting.figure.image) to accept "vanilla" Python data structures (e.g. tuples and lists) as arguments, as was the case before bokeh v3.0.

### Observed behavior

`Figure.image` renders as expected if passed a list containing a single 2D numpy array as its first argument. However, it does not render anything if said numpy array is replaced with a list of lists (or tuple of tuples, etc.). In this situation, errors are logged to the browser's console. In the example code below, uncommenting the `data = np.array(data)` line will cause the figure to render properly.

### Example code

```Python
import numpy as np
from bokeh.plotting import figure, show

data = [
    [0, 0],
    [1, 1],
]
# data = np.array(data)
p = figure()
p.image(image=[data], x=0, y=0, dw=10, dh=10, palette="Spectral11", level="image")
show(p)
```


### Stack traceback or browser console output

There are no Python errors. Here is the browser's console output:
```
11:49:03.008 [bokeh] setting log level to: 'info' bokeh-3.0.2.min.js:183:1311
11:49:03.054 Uncaught TypeError: can't access property "top_panel", n is undefined
    _measure_layout https://cdn.bokeh.org/bokeh/release/bokeh-3.0.2.min.js:574
    measure_layout https://cdn.bokeh.org/bokeh/release/bokeh-3.0.2.min.js:539
    compute_layout https://cdn.bokeh.org/bokeh/release/bokeh-3.0.2.min.js:539
    _after_resize https://cdn.bokeh.org/bokeh/release/bokeh-3.0.2.min.js:539
    after_resize https://cdn.bokeh.org/bokeh/release/bokeh-3.0.2.min.js:425
    _resize_observer https://cdn.bokeh.org/bokeh/release/bokeh-3.0.2.min.js:425
bokeh-3.0.2.min.js:574:8245
11:49:03.055 Uncaught (in promise) Error: expected a 2D array
    t https://cdn.bokeh.org/bokeh/release/bokeh-3.0.2.min.js:177
    assert https://cdn.bokeh.org/bokeh/release/bokeh-3.0.2.min.js:177
    _set_data https://cdn.bokeh.org/bokeh/release/bokeh-3.0.2.min.js:509
    set_data https://cdn.bokeh.org/bokeh/release/bokeh-3.0.2.min.js:376
    set_data https://cdn.bokeh.org/bokeh/release/bokeh-3.0.2.min.js:372
    lazy_initialize https://cdn.bokeh.org/bokeh/release/bokeh-3.0.2.min.js:372
bokeh-3.0.2.min.js:177:27
```

### Screenshots

_No response_
user: markgras
issue_12582: state: open
title: Unit tests occasionally fail in CI on MacOS and Python 3.10/3.11
body: ```py
=================================== FAILURES ===================================
____________________________ test__lifecycle_hooks _____________________________

ManagedServerLoop = <function ManagedServerLoop.<locals>.msl at 0x13ab85b20>

    @pytest.mark.skipif(sys.platform == "win32",
                        reason="Lifecycle hooks order different on Windows (TODO open issue)")
    def test__lifecycle_hooks(ManagedServerLoop: MSL) -> None:
        application = Application()
        handler = HookTestHandler()
        application.add(handler)
        with ManagedServerLoop(application, check_unused_sessions_milliseconds=30) as server:
            client_session = pull_session(session_id=ID("test__lifecycle_hooks"),
                                          url=url(server),
                                          io_loop=server.io_loop)
            client_doc = client_session.document
            assert len(client_doc.roots) == 1
    
            server_session = server.get_session('/', client_session.id)
            server_doc = server_session.document
            assert len(server_doc.roots) == 1
    
            # save for later, since doc.roots will be emptied after the session is closed
            client_hook_list = list(client_doc.roots[0].hooks)
            server_hook_list = list(server_doc.roots[0].hooks)
    
            client_session.close()
    
            # expire the session quickly rather than after the usual timeout
            server_session.request_expiration()
    
            server.io_loop.call_later(0.1, lambda: server.io_loop.stop())
    
            server.io_loop.start()
    
>       assert handler.hooks == [
            "server_loaded",
            "session_created",
            "modify",
            "next_tick",
            "timeout",
            "periodic",
            "session_destroyed",
            "server_unloaded",
        ]
E       AssertionError: assert ['server_load...eriodic', ...] == ['server_load...eriodic', ...]
E         At index 6 diff: 'server_unloaded' != 'session_destroyed'
E         Right contains one more item: 'server_unloaded'
E         Full diff:
E           [
E            'server_loaded',
E            'session_created',
E            'modify',
E            'next_tick',
E            'timeout',
E            'periodic',
E         -  'session_destroyed',
E            'server_unloaded',
E           ]

tests/unit/bokeh/server/test_server__server.py:396: AssertionError
```
user: mattpap
issue_12559: state: open
title: Automate force-updating main branch on release deploy
body: ref: https://github.com/bokeh/bokeh/discussions/12536

As discussed in the link, it is desirable that the `main` branch just always point at the newest (highest) full release tag. (i.e. if we were to release a 2.4.4 in the future, we should *not* update main in that case, because there are higher 3.x versions published)

During the release deploy job we should check to see if the current release tag is the newest (highest) release version, and execute the reset on main if so. 
user: bryevdv
issue_12558: state: open
title: Better label hygiene for issues / PRs
body: The release build job has a check to make sure that 
```
python scripts/milestone.py -a 3.0.1
```
is clean. For most releases it often isn't, and I have to manually update many issues' labels at the last minute. We should run this command on a regular basis to enforce better label hygiene in an ongoing way. 

Currently the script is geared towards inspecting issues in milestones. The simplest thing might be so simply have a CI job (nightly? weekly?) that runs the script for every open "version-ish" milestone. 

Alternatively, perhaps there is a GH action that could do some kind of check right when issues/PRs are closed to enforce label policies? 

Any thoughts, ideas, etc welcome @bokeh/dev
user: bryevdv
issue_12556: state: open
title: Release build job should publish a staged version of the docs
body: This would make it much easier to give the final built docs a review before running the release deploy. 

Could publish to specific `staged-3.0.0` etc but maybe OK to just use the generic `dev-3.x` etc as well. 
user: bryevdv
issue_12553: state: open
title: [FEATURE] Offline documentation 
body: ### Problem description

Having Bokeh documentation offline to remove network latency while intensively reading it trough. It also could save some laptop battery and let you to work in a deep forest near a lake without network connectivity 🙂

### Feature description

Due to the [conversation in Discource](https://discourse.bokeh.org/t/download-documentation/9696/6) i was recommended by @bryevdv  to create an issue to request publishing static documentation's files. They are exist already and are generated by CI i guess.

It isn't yet published as practically plots doesn't work without pulling js files from CDN , therefore, documentation usefulness is questionable. At the same time, a lot of examples and information is accessible, so it make sense to download it for offline usage.

**Improvement**
I don't know how long and difficult it takes but for improving usefulness plots could be replaced by images, i think readability become better with it, even if we can't interact with plots.


### Potential alternatives

No ideas.

### Additional information

_No response_
user: 4l1fe
issue_12523: state: open
title: Allow in-place edit of text on the canvas
body: This is to allow annotating (in a broad sense) data directly on the canvas. This is also related to allowing general purpose editing and selection or copying of text on the canvas. Details will follow as we discuss the requirements within the Holoviz ecosystem.
user: mattpap
issue_12486: state: open
title: [FEATURE] Control over the Toolbar icons collapse
body: ### Problem description

The Toolbar collapses icons into a 3 dots button ([before](https://discourse-uploads.bokeh.org/original/2X/f/f9e650d8297b5368064735643f66aae632165b93.png) and [after](https://discourse-uploads.bokeh.org/original/2X/5/5611cd5e044d248e50f80e758b6d295a27747e6f.png)).
This behavior confuses users.
Also, in some cases, [the figure is big enough ](https://discourse-uploads.bokeh.org/optimized/2X/6/6924b78a9680f49368451f6c8bcafbd8ef350c0e_2_345x235.png)to accommodate all icons but the collapse still occurs.


### Feature description

A boolean property to disable the collapse feature of the Toolbar.
For example:
Toolbar.collapse = True/False 

### Potential alternatives

Set a number for "must show" icons.
For Example:
Toolbar.number_of_icons = int

### Additional information

_No response_
user: ofir-frd
issue_12471: state: open
title: [BUG]  bokeh serve --dev not working
body: ### Software versions

Python version      :  3.10.0 (tags/v3.10.0:b494f59, Oct  4 2021, 19:00:18) [MSC v.1929 64 bit (AMD64)]
IPython version     :  8.5.0
Tornado version     :  6.2
Bokeh version       :  2.4.3
BokehJS static path :  C:\Users\pablo\AppData\Local\pypoetry\Cache\virtualenvs\my-bokeh-jAvr3NJn-py3.10\lib\site-packages\bokeh\server\static
node.js version     :  (not installed)
npm version         :  (not installed)
Operating system    :  Windows-10-10.0.22000-SP0


### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

I expect that when using the flag --dev the program detects saved changes in the code and refresh the application automatically.

### Observed behavior

The application launch and works, but after modifying and saving it, it is detected but it fails to update with this error:

`C:\Users\userAppData\Local\pypoetry\Cache\virtualenvs\my-bokeh-jAvr3NJn-py3.10\Scripts\python.exe: Error while finding module specification for '__main__' (ValueError: __main__.__spec__ is None)`

I guess is a windows-poetry environment related issue, as I guess this flag is working for everyone else, but I'm not sure.

### Example code

```Python
Any example like server/app/dash will reproduce this error.
```


### Stack traceback or browser console output

`C:\Users\userAppData\Local\pypoetry\Cache\virtualenvs\my-bokeh-jAvr3NJn-py3.10\Scripts\python.exe: Error while finding module specification for '__main__' (ValueError: __main__.__spec__ is None)`

### Screenshots

_No response_
user: PabloRuizCuevas
issue_12469: state: open
title: [BUG] IndexError when using bokeh serve --dev without app directory or script
body: ### Software versions

Python version      :  3.10.6 (main, Aug 10 2022, 11:40:04) [GCC 11.3.0]
IPython version     :  (not installed)
Tornado version     :  6.2
Bokeh version       :  3.0.0.dev19
BokehJS static path :  /home/stefan/.local/lib/python3.10/site-packages/bokeh/server/static
node.js version     :  v16.16.0
npm version         :  8.11.0
Operating system    :  Linux-5.15.0-48-generic-x86_64-with-glibc2.35


### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

The command line with `--dev` option might not even make sense with an empty document, but IMO it should either
- silently pass on autoreloading target files, or
- print a useful error message

### Observed behavior

```
Traceback (most recent call last):
  File "/home/stefan/.local/bin/bokeh", line 8, in <module>
    sys.exit(main())
  File "/home/stefan/.local/lib/python3.10/site-packages/bokeh/__main__.py", line 59, in main
    _main(sys.argv)
  File "/home/stefan/.local/lib/python3.10/site-packages/bokeh/command/bootstrap.py", line 109, in main
    ret = args.invoke(args)
  File "/home/stefan/.local/lib/python3.10/site-packages/bokeh/command/subcommands/serve.py", line 946, in invoke
    find_autoreload_targets(args.files[0])
IndexError: list index out of range
```

### Example code

```Python
export BOKEH_DEV=true; bokeh serve --dev
```


### Stack traceback or browser console output

_No response_

### Screenshots

_No response_
user: salsasepp
issue_12464: state: open
title: [FEATURE] Filtering mechanism for LabelSet
body: ### Problem description

hey, thanks for the hard work in maintaing this lib!.
 Currently there's no way to filter labels, so could the parameter "view" be added to LabelSet to filter the rendering of text labels?.


Thanks!

### Feature description

view could act as CDSview

### Potential alternatives

idk

### Additional information

_No response_
user: missilesdeveces
issue_12452: state: open
title: [BUG] Cannot terminate busy pool processes in Bokeh app
body: ### Software versions

Python: 3.9.7
Bokeh version: 2.4.3
Tornado version: 6.1
Operating systems: Linux (Fedora 36, RHEL7, Ubuntu 18.04)

### Expected behavior

In my application I have to put a timeout on a pool of processes executing jobs in parallel so that it is terminated if it takes too long. It works fine in a standalone script.

### Observed behavior

In a Bokeh app the pool fails to terminate. The issue is serious because the main process hangs so that the app not only becomes unresponsive in the current session but cannot serve new sessions. The only solution it seems is to restart the server.

The minimal example provided works as expected when executed directly, but blocks at `p.terminate()` in the context of a Bokeh app (see output). The two processes that are still busy when `p.terminate()` is reached are not terminated (they fail to join in the _terminate_pool function of the [pool.py](https://github.com/python/cpython/blob/3.9/Lib/multiprocessing/pool.py) module).

I do not think it's a usage issue or a misuse of python's multiprocessing but my apologies if it turns out that it is.

Note: originally filed an issue [in Panel](https://github.com/holoviz/panel/issues/3938) but it's not related to Panel itself. I hope it's not a consequence that is inherent to the implementation of Bokeh server.

### Example code

```Python
import multiprocessing as mp
import time
import os
from bokeh.io import curdoc
from bokeh.models import Button


def task(num):
    print('Start', os.getpid(), num)
    time.sleep(num)
    print('End', os.getpid(), num)


def callback(new=None):

    p = mp.Pool(processes=4)
    res = p.map_async(task, [1, 2, 3, 4])
    try:
        res.get(timeout=2.5)
    except mp.context.TimeoutError:
        print('Timeout')
    p.terminate()
    print('Pool terminated')


button = Button()
button.on_click(callback)

curdoc().add_root(button)

if __name__ == '__main__':
    callback()
```


### Stack traceback or browser console output

```
$ python script.py
Start 6209 1
Start 6210 2
Start 6211 3
Start 6212 4
End 6209 1
End 6210 2
Timeout
Pool terminated

$ bokeh serve script.py
2022-10-02 21:37:23,760 Starting Bokeh server version 2.4.3 (running on Tornado 6.1)
2022-10-02 21:37:23,761 User authentication hooks NOT provided (default user enabled)
2022-10-02 21:37:23,765 Bokeh app running at: http://localhost:5006/script
2022-10-02 21:37:23,766 Starting Bokeh server with process id: 7744
2022-10-02 21:37:28,903 404 GET /favicon.ico (127.0.0.1) 2.51ms
2022-10-02 21:37:28,904 WebSocket connection opened
2022-10-02 21:37:28,905 ServerConnection created
Start 7808 1
Start 7809 2
Start 7810 3
Start 7811 4
End 7808 1
End 7809 2
Timeout
Received signal 15, shutting down
Received signal 15, shutting down
End 7810 3
End 7811 4
# Hangs
```
user: TheoMathurin
issue_12450: state: open
title: Allow to configure per component stylesheets
body: We already allow to configure per instances (of a component) stylesheets, but it would be inefficient to style all components of a given kind like this. This is specifically needed for panel, but will useful in general.

user: mattpap
issue_12430: state: open
title: [BUG] Step glyphs do not support linked brushing
body: ### Software versions

Python version      :  3.9.13 | packaged by conda-forge | (main, May 27 2022, 16:56:21) 
IPython version     :  8.5.0
Tornado version     :  6.2
Bokeh version       :  3.0.0.dev18+15.gc0b9834e
BokehJS static path :  /home/iant/github/bokeh/src/bokeh/server/static
node.js version     :  v16.15.0
npm version         :  8.19.2
Operating system    :  Linux-5.11.0-37-generic-x86_64-with-glibc2.31


### Browser name and version

Not browser-specific

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

Linked brushing (selection of indices to render) does not work at all for canvas or WebGL output backends. It is implemented incorrectly for canvas, and not at all for WebGL.

### Observed behavior

See screenshot below.

### Example code

```Python
from bokeh.models import ColumnDataSource
from bokeh.plotting import figure, row, show
import numpy as np

x = np.arange(-3, 4)
y = x**2
source = ColumnDataSource(data=dict(x=x, y=y))

ps = []
for backend in ["canvas", "webgl"]:
    p = figure(tools="box_select,reset", width=300, height=250, title=backend, output_backend=backend)
    p.step('x', 'y', source=source, mode="center", line_width=8, alpha=0.8)
    p.circle('x', 'y', source=source, fill_color="orange", line_color="black", size=10)
    ps.append(p)

show(row(ps))
```


### Stack traceback or browser console output

_No response_

### Screenshots

![Screenshot 2022-09-27 at 09-45-04 Bokeh Plot](https://user-images.githubusercontent.com/580326/192479304-9260340b-53d7-4425-aa9b-c516310e98ec.png)

user: ianthomas23
issue_12414: state: open
title: [BUG] Incorrect indentation with bokeh.embed.components
body: ### Software versions

Python: 3.8.10
bokeh: 2.4.3, 3.0.0.dev18

### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

With `components` from `bokeh.embed`, the script's first two lines and last line should not be indented:

```
<script type="text/javascript">
(function() {
  ...
})();
</script>
```

### Observed behavior

The first two lines, and the last line, are strangely indented:

```
    <script type="text/javascript">
        (function() {
  ...
})();
    </script>
```

The indentation appears to be correct for all other lines.

### Example code

```Python
from bokeh.plotting import figure, show
from bokeh.embed import components
x = [1, 2]
y = [6, 7]
p = figure(title="Simple line example", x_axis_label='x', y_axis_label='y')
p.line(x, y, legend_label="Temp.", line_width=2)
script, div = components(p)
print(script)
```
```


### Stack traceback or browser console output

_No response_

### Screenshots

_No response_
user: ErikAtApex
issue_12399: state: open
title: [BUG] Toggle class does not honor setting background color
body: ### Software versions

Python version      :  3.7.13 (default, Mar 18 2022, 10:22:19)
IPython version     :  7.34.0
Tornado version     :  6.2
Bokeh version       :  2.4.3
BokehJS static path :  /home/echodyne.int/janet/jupiter/ver8/lib/python3.7/site-packages/bokeh/server/static
node.js version     :  v14.19.1
npm version         :  6.14.16
Operating system    :  Linux-5.4.0-125-generic-x86_64-with-debian-buster-sid

### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

When setting the background property of a toggle, I expected the background color to change

### Observed behavior

The toggles ignore the setting completely

### Example code

```Python
from bokeh.models import BoxAnnotation, Toggle
from bokeh.plotting import figure

PINK = "#CC79A7"
GREEN = "#009E73"

output_file("styling_visible_annotation_with_interaction.html")

p = figure(width=600, height=200, tools='')
p.line([1, 2, 3], [1, 2, 1], line_color="#0072B2")
pink_line = p.line([1, 2, 3], [2, 1, 2], line_color=PINK)

green_box = BoxAnnotation(left=1.5, right=2.5, fill_color=GREEN, fill_alpha=0.1)
p.add_layout(green_box)

# I expected the color of toggle1 to be the same color as the green box
toggle1 = Toggle(label="Green Box", button_type="default", active=True, background=GREEN)
toggle1.js_link('active', green_box, 'visible')

# I expected the color of toggle2 to be the same color as the pink line
toggle2 = Toggle(label="Pink Line", button_type="default", active=True, background=PINK)
toggle2.js_link('active', pink_line, 'visible')

show(layout([p], [toggle1, toggle2]))
```


### Stack traceback or browser console output

_No response_

### Screenshots

_No response_
user: j-carson
issue_12390: state: open
title: Box zoom and selection should have auto-direction mode
body: Similar to other plotting libraries, the Box zoom and selection tools should have an "auto" mode where moving very little in one direction activates the "full-range" mode in the cross direction. As a concrete example, if the box zoom is active and the user drags directly to the right without moving up or down (within some small tolerance) then the zoom should be an x-zoom tool that only zooms the x-range. If the users moves up or down during the interaction then the tool should change to a box zoom in both directions.  Moving back near the original vertical start location (i.e. `delta_sy ~ 0`) should switch back to x-zoom mode. 

The visual overlay should change appropriately to indicate whatever mode of operation is active.

There will be potential interactions with aspect-matching to sort out. Probably just always honor aspect matching, even in "auto" mode. 

ref: #5135 
user: bryevdv
labels: tag: tools: Related to the toolbar and individual tools
issue_12384: state: open
title: Figure out why regression test for issue #11413 fails
body: This is an ongoing issue. Most likely it's an issue with idle reporting.
```
✗ Bug
 └┬─ in issue #11413
  └── doesn't allow to remove an annotation element associated with a tile renderer
timeout
```
user: mattpap
issue_12382: state: open
title: Finalize migration to SVG icons
body: The following icons still need to be migrated:
```
$ git grep '\.tool-icon(--'
bokehjs/src/less/icons.less:  .tool-icon(--bokeh-icon-replace-mode, "ReplaceMode");
bokehjs/src/less/icons.less:  .tool-icon(--bokeh-icon-append-mode, "AppendMode");
bokehjs/src/less/icons.less:  .tool-icon(--bokeh-icon-intersect-mode, "IntersectMode");
bokehjs/src/less/icons.less:  .tool-icon(--bokeh-icon-subtract-mode, "SubtractMode");
bokehjs/src/less/icons.less:  .tool-icon(--bokeh-icon-clear-selection, "ClearSelection");
bokehjs/src/less/icons.less:  .tool-icon(--bokeh-icon-range, "Range");
bokehjs/src/less/icons.less:  .tool-icon(--bokeh-icon-polygon-select, "PolygonSelect");
bokehjs/src/less/icons.less:  .tool-icon(--bokeh-icon-wheel-pan, "WheelPan");
bokehjs/src/less/icons.less:  .tool-icon(--bokeh-icon-wheel-zoom, "WheelZoom");
bokehjs/src/less/icons.less:  .tool-icon(--bokeh-icon-box-edit, "BoxEdit");
bokehjs/src/less/icons.less:  .tool-icon(--bokeh-icon-freehand-draw, "FreehandDraw");
bokehjs/src/less/icons.less:  .tool-icon(--bokeh-icon-poly-draw, "PolyDraw");
bokehjs/src/less/icons.less:  .tool-icon(--bokeh-icon-point-draw, "PointDraw");
bokehjs/src/less/icons.less:  .tool-icon(--bokeh-icon-poly-edit, "PolyEdit");
bokehjs/src/less/icons.less:  .tool-icon(--bokeh-icon-line-edit, "LineEdit");
```
user: mattpap
issue_12375: state: open
title: [FEATURE] Set axis label at a fixed distance from the axis
body: ### Problem description

The only way to set y-axis label horizontal position is using `axis_label_standoff`, which sets the distance between the axis label and the major tick labels. Therefore, when the tick labels change so does the axis label position, which is usually the case when the y-range changes (typically due to data update or when zooming in/out). This makes it impossible to align y-axis labels when having multiple figures in a column layout.

### Feature description

There should be possible to set axis labels at a fixed distance from the axis. This can be done by providing more options for setting y-axis label standoff, beside `axis_label_standoff`, such as `axis_label_standoff_axis` (distance from the axis), and maybe `axis_label_standoff_tick_mark` (distance from the axis (major) tick marks). This is initially intended for the y-axis, but it can be useful to have for the x-axis as well.

### Potential alternatives

None.

### Additional information

An example of the issue where two y-axis labels cannot be aligned, even though the y-axes and x-axes ranges are. I used bokeh version 2.4.3.

```
from bokeh.plotting import figure, show
from bokeh.layouts import column

p1 = figure(plot_height=200, y_axis_label='Label 1')
p1.line([0, 1, 2], [1, 2, 3])
p1.yaxis.axis_label_standoff = 5

p2 = figure(plot_height=200, y_axis_label='Label 2')
p2.line([0, 1, 2], [0.001, 0.002, 0.003])
p2.yaxis.axis_label_standoff = 5

show(column(p1, p2))
```

![not_aligned_labels](https://user-images.githubusercontent.com/25090598/189752779-a32e6a40-62e5-4090-9683-da7260cad3e8.png)
user: bkos
issue_12367: state: open
title: [FEATURE] MultiScatter
body: ### Problem description

For me, this applies primarily to linking of spatial and transient data. At each location in plan view, lots of datapoints exist over time. Sometimes I want to plot these transient datapoints with lines: MultiLine is beautifully equipped to do this:

```
data = {'Location': ['loc1','loc2'],'Northing':[0,4],'Easting':[3,4],'Time':[[0,1,2,3],[2,3,4]],'Value':[[4,3,6],[1,3,2]]}
src = ColumnDataSource(data)
plan_view = figure()
plan_view.scatter(x='Easting',y='Northing',source=src)
time_plot = figure()
time_plot.multiline(xs='Time',ys='Value',source=src)
```
... And so on. CustomJS then does stuff with user defined selections, and hovertool allows the user to link the space and time components visually. It's awesome. 

But sometimes, the transient data is discontinuous enough to warrant plotting as points, not connected lines. For these I've got an entirely different setup using Scatter and a lot of annoying, sub-optimal CustomJS doing basically an inner join (through alasql) operation to link two separate CDSs (one for the spatial, one for the transient). It works but complicates setup and is noticeably laggier for bigger datasets.


### Feature description

What I dream of, is a dead-on clone of MultiLine, but instead of rendering lines, it just renders markers. That's basically it. I browsed source code etc. looking to see if I could do this myself and it seems WAY out of my league. I would be extremely, extremely happy to be guided on this, and of course if someone is willing to simply do it for me... well I'd also be extremely happy about that too.

### Potential alternatives

As mentioned in problem description, I have my own workaround but it's very suboptimal and over-complicated.

### Additional information

_No response_
user: gmerritt123
issue_12366: state: open
title: Robustify data acquisition in color mappers
body: fixes #12355

user: mattpap
issue_12355: state: open
title: Colormapping multiple images
body: ### Discussed in https://github.com/bokeh/bokeh/discussions/12354

<div type='discussions-op-text'>

<sup>Originally posted by **ianthomas23** September  7, 2022</sup>
`Figure.image()` can take multiple images (multiple 2D arrays) that are colormapped using the same `ColorMapper`. This can give rise to some strange results. Consider this example which has one array with values in the range 0 to 1, and a second with values in the range 1 to 2.
```python
from bokeh.models import ColorBar, LinearColorMapper
from bokeh.plotting import figure, show
import numpy as np

rng = np.random.default_rng(3841)
n = 20
data0 = rng.random((n, n))
data1 = data0 + 1.0

p = figure(width=600, height=300)
mapper = LinearColorMapper(palette="Spectral10")

p.image(image=[data0, data1], x=[0, 1.1], y=0, dw=1, dh=1, color_mapper=mapper)

cbar = ColorBar(color_mapper=mapper)
p.add_layout(cbar, place="right")

show(p)
```
This produces
![Screenshot 2022-09-07 at 12-29-34 Bokeh Plot](https://user-images.githubusercontent.com/580326/188868116-86d84864-f132-4643-8953-cc14c9ac9b13.png)
Here each array is separately colormapped so they both use the full palette and are rendered with the same colors. The colorbar shows the mapping used for the second image so the limits are approximately 1 to 2. If you swap the order of the 2 arrays then the output is the same except that the colorbar limits are approximately 0 to 1.

I can get what I consider to be the correct output using e.g.
```python
mapper = LinearColorMapper(palette="Spectral10", low=0, high=2)
```
which produces
![Screenshot 2022-09-07 at 12-29-54 Bokeh Plot](https://user-images.githubusercontent.com/580326/188868984-b490da6a-827c-458f-b6b0-b0438a6bd6e5.png)
which essentially forces the colormapper to ignore the actual data values. This wouldn't work for `EqHistColorMapper` as it always needs to scan the data.

This isn't really a problem caused by `Figure.image` taking multiple arrays, you can obtain the same result using two `Figure.image` calls that take different arrays but the same colormapper. But then at least you can specify different colormappers for each `Figure.image` call whereas the single `Figure.image` call with multiple arrays is obliged to use a single colormapper.

`ContinuousColorMapper` has a `domain` property that I think is relevant here, but if I replace the single `p.image` call above with
```python
r0 = p.image(image=[data0], x=0.0, y=0, dw=1, dh=1, color_mapper=mapper)
r1 = p.image(image=[data1], x=1.1, y=0, dw=1, dh=1, color_mapper=mapper)
mapper.domain = [r0, r1]
```
then I still see the same result.

I am not sure if this is a bug or a documentation issue, or user error, or whatever, hence I have created it as a discussion.</div>
user: ianthomas23
issue_12350: state: open
title: [BUG] Using Cypress to perform automation tests, is not functional
body: ### Software versions

Node 16.16.0
Cypress 10.7.0
Bokeh 2.4.3

### Browser name and version

Tested on Chrome and Firefox

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

Cypress should be able to select an area in the plot.



### Observed behavior

Cypress is not functional in the plot

### Example code

```Python
I'm using the trigger function of cypress to perform click events like mousedown, mousemove, mouseup:

cy.get('canvas') //selecting any canvas element (each plot contains 2 canvas elements)
   .eq(0) //selecting the first of two canvas element (tried selecting 2nd canvas element too)
   .trigger('mousedown', 100, 100, {force: true}) 
   .trigger('mousemove', 200, 200, {force: true})
   .trigger('mouseup', 200, 200, {force: true}) //force: true has been added to be sure the event 
                                                //will be performed if the canvas is being 
                                                //covered from another element

Cypress trigger documentation:
https://docs.cypress.io/api/commands/trigger
```


### Stack traceback or browser console output

_No response_

### Screenshots

I used cypress to draw a triangle in a simple canvas element and works fine:

![drawTriangle](https://user-images.githubusercontent.com/44839448/188587410-93a50ac3-6ec8-4e3a-af25-8946355975d1.png)

In Bokeh, he code seems to be working in the plot but is not functional:

mousedown
![mousedown](https://user-images.githubusercontent.com/44839448/188589728-be5fa0f2-9c96-45fe-a44a-f79aeb56f26e.png)

mousemove
![mousemove](https://user-images.githubusercontent.com/44839448/188589651-87bea3f8-c54b-48c1-a221-a9621b7e6de2.png)

mouseup
![mouseup](https://user-images.githubusercontent.com/44839448/188589861-c2c63400-3fdc-4501-88be-4c0283ce7000.png)

*_After a test has been completed in Cypress, the Test Runner doesn't display the content of a canvas due to memory issue. That's why the plot is blank._

user: CharalamposSam
issue_12345: state: open
title: [FEATURE] Provide API for accessing callbacks registered to a widget
body: ### Problem description

If one wants to temporarily inhibit callbacks on a widget, the callbacks need to be removed and then added back. The current method of removing an `on_change` callback is to use `remove_on_change(attr)`. However, the latter method does not return the callback that was removed, so if a user wants to add the callback again, knowledge of what callback to put back must come from elsewhere. 

For more, see [this Discourse thread](https://discourse.bokeh.org/t/boomerang-update-avoidance/9506) 

### Feature description

Add a return value to `remove_on_change` that returns the callback that was removed.

### Potential alternatives

Add a public interface for `widget._callbacks` (e.g. a property getter).

### Additional information

_No response_
user: lunarspectrum
issue_12312: state: open
title: [BUG] Tooltips' Positions are Mismatched When Using CSS Property Zoom
body: ### Software versions

Python version      :  3.9.7 (default, Sep 16 2021, 16:59:28) [MSC v.1916 64 bit (AMD64)]
IPython version     :  7.29.0
Tornado version     :  6.1
Bokeh version       :  2.4.3
BokehJS static path :  C:\ProgramData\Anaconda3\lib\site-packages\bokeh\server\static
node.js version     :  v16.14.2
npm version         :  8.5.0
Operating system    :  Windows-10-10.0.22000-SP0

### Browser name and version

Chrome 104.0.5112.81

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

The tooltips' postions are correct when I hover on glyphs, if the following css rule is applied and is taking effect:

```css
@media (min-width: 2000px) {
  html {zoom: 2};
}
```

### Observed behavior

When the above css rule (zoom) is taking effect, i.e., if I use a high-resolution screen (>2000px) or zoom out the page with the browser, the ploy is showing correct, but when I hover on glyphs, no tooltips are showing. And when I hover on some other random postions, some tooltips are showing. 

You can reproduce this by visiting the following page: https://xovee.cn

Also I provide a example code below, you can see that I add a css rule in the begining. 

### Example code

```Python
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Bokeh Plot</title>
    <script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-2.4.3.min.js"></script>
    <script type="text/javascript">
        Bokeh.set_log_level("info");
    </script>
    <style>
      body {zoom: 1.2;}
    </style>
  </head>
  <body>
    <div class="bk-root" id="927262b8-6435-4aee-a4b8-aa6755c0f2cd" data-root-id="1004"></div>
  
    <script type="application/json" id="1416">
      {"22f53ee2-9ac0-4bfd-a331-ae52e3411ca5":{"defs":[],"roots":{"references":[{"attributes":{"coordinates":null,"data_source":{"id":"1002"},"glyph":{"id":"1041"},"group":null,"hover_glyph":null,"muted_glyph":{"id":"1043"},"nonselection_glyph":{"id":"1042"},"view":{"id":"1045"}},"id":"1044","type":"GlyphRenderer"},{"attributes":{"coordinates":null,"group":null,"text":" &gt; Middle School","text_color":"gray","text_font_size":"12px","x":1220227200000.0,"y":68.03999999999999},"id":"1054","type":"Label"},{"attributes":{"line_alpha":{"value":0.1},"line_color":{"value":"gray"},"line_dash":{"value":"dashed"},"line_width":{"value":1.3},"x0":{"field":"date"},"x1":{"field":"date"},"y0":{"value":0},"y1":{"value":75.6}},"id":"1048","type":"Segment"},{"attributes":{"source":{"id":"1002"}},"id":"1038","type":"CDSView"},{"attributes":{},"id":"1066","type":"BasicTickFormatter"},{"attributes":{"line_color":{"value":"gray"},"line_dash":{"value":"dashed"},"line_width":{"value":1.3},"x0":{"field":"date"},"x1":{"field":"date"},"y0":{"value":0},"y1":{"value":75.6}},"id":"1047","type":"Segment"},{"attributes":{},"id":"1072","type":"UnionRenderers"},{"attributes":{"fill_alpha":{"value":0.1},"fill_color":{"value":"green"},"hatch_alpha":{"value":0.1},"line_alpha":{"value":0.1},"line_color":{"value":"white"},"size":{"value":10},"x":{"field":"date"},"y":{"field":"citation"}},"id":"1035","type":"Circle"},{"attributes":{"coordinates":null,"group":null,"text":" &gt; Born","text_color":"gray","text_font_size":"12px","x":826588800000.0,"y":68.03999999999999},"id":"1052","type":"Label"},{"attributes":{},"id":"1073","type":"Selection"},{"attributes":{"callback":null,"renderers":[{"id":"1050"}],"tooltips":"\n    &lt;div&gt;\n        &lt;span&gt;&lt;b&gt;@role&lt;/b&gt;&lt;/span&gt;&lt;br&gt;\n        &lt;span&gt;@affiliation&lt;/span&gt;&lt;br&gt;\n        &lt;span&gt;@duration&lt;/span&gt;&lt;address&gt;@address&lt;/address&gt;\n    &lt;/div&gt;\n\n"},"id":"1061","type":"HoverTool"},{"attributes":{},"id":"1067","type":"AllLabels"},{"attributes":{"coordinates":null,"data_source":{"id":"1003"},"glyph":{"id":"1047"},"group":null,"hover_glyph":null,"muted_glyph":{"id":"1049"},"nonselection_glyph":{"id":"1048"},"view":{"id":"1051"}},"id":"1050","type":"GlyphRenderer"},{"attributes":{"line_alpha":{"value":0.2},"line_color":{"value":"gray"},"line_dash":{"value":"dashed"},"line_width":{"value":1.3},"x0":{"field":"date"},"x1":{"field":"date"},"y0":{"value":0},"y1":{"value":75.6}},"id":"1049","type":"Segment"},{"attributes":{"source":{"id":"1003"}},"id":"1051","type":"CDSView"},{"attributes":{"coordinates":null,"group":null,"text":" # citation: 113","text_color":"black","text_font_size":"12px","x":0,"x_units":"screen","y":5,"y_units":"screen"},"id":"1059","type":"Label"},{"attributes":{"coordinates":null,"group":null,"text":" &gt; Primary School","text_color":"gray","text_font_size":"12px","x":1030838400000.0,"y":68.03999999999999},"id":"1053","type":"Label"},{"attributes":{},"id":"1069","type":"AllLabels"},{"attributes":{"callback":null,"renderers":[{"id":"1037"}],"tooltips":"\n    &lt;div&gt;\n        &lt;span style='font-size: 12px; font-weight: bold;'&gt;@title&lt;/span&gt;&lt;br&gt;\n        &lt;span&gt;@author&lt;/span&gt;&lt;br&gt;\n        &lt;span style='display: inline-block;'&gt;@venue&lt;/span&gt;\n        &lt;span style='float: right;'&gt;Citation: @citation&lt;/span&gt;\n        &lt;div style='clear: both; margin-bottom: 3px;'&gt;&lt;/div&gt;\n    &lt;/div&gt;\n"},"id":"1060","type":"HoverTool"},{"attributes":{"coordinates":null,"group":null,"text":" &gt; PhD Student","text_color":"gray","text_font_size":"12px","x":1630454400000.0,"y":68.03999999999999},"id":"1058","type":"Label"},{"attributes":{"coordinates":null,"group":null,"text":" &gt; High School","text_color":"gray","text_font_size":"12px","x":1314835200000.0,"y":68.03999999999999},"id":"1055","type":"Label"},{"attributes":{"data":{"address":["Zizhou, Shaanxi, China","Zizhou, Shaanxi, China","Zizhou, Shaanxi, China","Yulin, Shaanxi, China","Chengdu, Sichuan, China","Chengdu, Sichuan, China","Chengdu, Sichuan, China"],"affiliation":["Home","Zizhou Experimental Primary School","Zizhou No.2 Middle School","Yulin High School","University of Electronic Science and Technology of China (UESTC)","University of Electronic Science and Technology of China (UESTC)","University of Electronic Science and Technology of China (UESTC)"],"date":[826588800000.0,1030838400000.0,1220227200000.0,1314835200000.0,1409529600000.0,1535760000000.0,1630454400000.0],"duration":["1996-2002","2002-2008","2008-2011","2011-2014","2014-2018","2018-2021","2021-"],"role":["Born","Primary School","Middle School","High School","Bachelor","Master","PhD Student"]},"selected":{"id":"1073"},"selection_policy":{"id":"1072"}},"id":"1003","type":"ColumnDataSource"},{"attributes":{"coordinates":null,"group":null,"text":" &gt; Bachelor","text_color":"gray","text_font_size":"12px","x":1409529600000.0,"y":68.03999999999999},"id":"1056","type":"Label"},{"attributes":{"coordinates":null,"group":null,"text":" &gt; Master","text_color":"gray","text_font_size":"12px","x":1535760000000.0,"y":68.03999999999999},"id":"1057","type":"Label"},{"attributes":{"source":{"id":"1002"}},"id":"1045","type":"CDSView"},{"attributes":{"line_alpha":{"value":0.2},"x0":{"field":"date"},"x1":{"field":"date"},"y0":{"value":0},"y1":{"field":"citation"}},"id":"1043","type":"Segment"},{"attributes":{"x0":{"field":"date"},"x1":{"field":"date"},"y0":{"value":0},"y1":{"field":"citation"}},"id":"1041","type":"Segment"},{"attributes":{"mantissas":[1,2,5],"max_interval":500.0,"num_minor_ticks":0},"id":"1074","type":"AdaptiveTicker"},{"attributes":{"base":60,"mantissas":[1,2,5,10,15,20,30],"max_interval":1800000.0,"min_interval":1000.0,"num_minor_ticks":0},"id":"1075","type":"AdaptiveTicker"},{"attributes":{"days":[1,8,15,22]},"id":"1079","type":"DaysTicker"},{"attributes":{"background_fill_color":null,"below":[{"id":"1015"}],"border_fill_color":null,"center":[{"id":"1018"},{"id":"1022"},{"id":"1052"},{"id":"1053"},{"id":"1054"},{"id":"1055"},{"id":"1056"},{"id":"1057"},{"id":"1058"},{"id":"1059"}],"height":280,"left":[{"id":"1019"}],"outline_line_width":0,"renderers":[{"id":"1037"},{"id":"1044"},{"id":"1050"}],"sizing_mode":"scale_width","title":{"id":"1005"},"toolbar":{"id":"1026"},"toolbar_location":null,"width":1080,"x_range":{"id":"1007"},"x_scale":{"id":"1011"},"y_range":{"id":"1009"},"y_scale":{"id":"1013"}},"id":"1004","subtype":"Figure","type":"Plot"},{"attributes":{},"id":"1070","type":"UnionRenderers"},{"attributes":{"base":24,"mantissas":[1,2,4,6,8,12],"max_interval":43200000.0,"min_interval":3600000.0,"num_minor_ticks":0},"id":"1076","type":"AdaptiveTicker"},{"attributes":{"months":["%b %Y"]},"id":"1031","type":"DatetimeTickFormatter"},{"attributes":{},"id":"1085","type":"YearsTicker"},{"attributes":{"coordinates":null,"formatter":{"id":"1031"},"group":null,"major_label_policy":{"id":"1069"},"major_tick_line_color":"silver","ticker":{"id":"1016"}},"id":"1015","type":"DatetimeAxis"},{"attributes":{"coordinates":null,"data_source":{"id":"1002"},"glyph":{"id":"1034"},"group":null,"hover_glyph":{"id":"1039"},"level":"annotation","muted_glyph":{"id":"1036"},"nonselection_glyph":{"id":"1035"},"view":{"id":"1038"}},"id":"1037","type":"GlyphRenderer"},{"attributes":{"end":1719705600000.0,"start":1377993600000.0},"id":"1007","type":"Range1d"},{"attributes":{},"id":"1071","type":"Selection"},{"attributes":{"days":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31]},"id":"1077","type":"DaysTicker"},{"attributes":{"days":[1,4,7,10,13,16,19,22,25,28]},"id":"1078","type":"DaysTicker"},{"attributes":{"end":75.6},"id":"1009","type":"Range1d"},{"attributes":{"line_alpha":{"value":0.1},"x0":{"field":"date"},"x1":{"field":"date"},"y0":{"value":0},"y1":{"field":"citation"}},"id":"1042","type":"Segment"},{"attributes":{"fill_alpha":{"value":0.2},"fill_color":{"value":"green"},"hatch_alpha":{"value":0.2},"line_alpha":{"value":0.2},"line_color":{"value":"white"},"size":{"value":10},"x":{"field":"date"},"y":{"field":"citation"}},"id":"1036","type":"Circle"},{"attributes":{"num_minor_ticks":5,"tickers":[{"id":"1074"},{"id":"1075"},{"id":"1076"},{"id":"1077"},{"id":"1078"},{"id":"1079"},{"id":"1080"},{"id":"1081"},{"id":"1082"},{"id":"1083"},{"id":"1084"},{"id":"1085"}]},"id":"1016","type":"DatetimeTicker"},{"attributes":{"months":[0,6]},"id":"1084","type":"MonthsTicker"},{"attributes":{"days":[1,15]},"id":"1080","type":"DaysTicker"},{"attributes":{"coordinates":null,"group":null,"text":"Xovee's Timeline"},"id":"1005","type":"Title"},{"attributes":{"months":[0,1,2,3,4,5,6,7,8,9,10,11]},"id":"1081","type":"MonthsTicker"},{"attributes":{},"id":"1011","type":"LinearScale"},{"attributes":{"months":[0,2,4,6,8,10]},"id":"1082","type":"MonthsTicker"},{"attributes":{},"id":"1013","type":"LinearScale"},{"attributes":{"months":[0,4,8]},"id":"1083","type":"MonthsTicker"},{"attributes":{"axis":{"id":"1015"},"coordinates":null,"group":null,"ticker":null},"id":"1018","type":"Grid"},{"attributes":{"axis_line_color":null,"coordinates":null,"formatter":{"id":"1066"},"group":null,"major_label_policy":{"id":"1067"},"major_tick_line_color":null,"minor_tick_line_color":null,"ticker":{"id":"1020"}},"id":"1019","type":"LinearAxis"},{"attributes":{"data":{"author":["Ting Zhong, Haoyang Yu, Rongfan Li, &lt;b&gt;Xovee Xu&lt;/b&gt;*, Xucheng Luo, and Fan Zhou","Liu Yu, &lt;b&gt;Xovee Xu&lt;/b&gt;, Ting Zhong, Goce Trajcevski, and Fan Zhou*","Guanyu Wang, &lt;b&gt;Xovee Xu&lt;/b&gt;, Ting Zhong*, and Fan Zhou","Haoyang Yu, &lt;b&gt;Xovee Xu&lt;/b&gt;, Ting Zhong, and Fan Zhou*","Zhiyuan Wang, &lt;b&gt;Xovee Xu&lt;/b&gt;, Goce Trajcevski, Kunpeng Zhang, Ting Zhong, and Fan Zhou*","&lt;b&gt;Xovee Xu&lt;/b&gt;, Fan Zhou*, Kunpeng Zhang, and Siyuan Liu","&lt;b&gt;Xovee Xu&lt;/b&gt;, Ting Zhong, Ce Li, Goce Trajcevski, and Fan Zhou*","Fan Zhou, Pengyu Wang, &lt;b&gt;Xovee Xu&lt;/b&gt;*, Wenxin Tai, and Goce Trajcevski","Fan Zhou, Ce Li, &lt;b&gt;Xovee Xu&lt;/b&gt;, Leyuan Liu*, and Goce Trajcevski","&lt;b&gt;Xovee Xu&lt;/b&gt;, Fan Zhou*, Kunpeng Zhang, Siyuan Liu, and Goce Trajcevski","Guanyu Wang, Ting Zhong, &lt;b&gt;Xovee Xu&lt;/b&gt;, Kunpeng Zhang, Fan Zhou*, and Yong Wang","Fan Zhou, Liu Yu, &lt;b&gt;Xovee Xu&lt;/b&gt;*, and Goce Trajcevski","Fan Zhou, &lt;b&gt;Xovee Xu&lt;/b&gt;*, Goce Trajcevski, and Kunpeng Zhang","Fan Zhou, Xin Jing, &lt;b&gt;Xovee Xu&lt;/b&gt;, Ting Zhong, Goce Trajcevski, and Jin Wu*","Fan Zhou, Zijing Wen, Ting Zhong*, Goce Trajcevski, &lt;b&gt;Xovee Xu&lt;/b&gt;, and Leyuan Liu*","Fan Zhou, Xiuxiu Qi, &lt;b&gt;Xovee Xu&lt;/b&gt;, Jiahao Wang*, Ting Zhong, and Goce Trajcevski","Fan Zhou, &lt;b&gt;Xovee Xu&lt;/b&gt;, Ce Li, Goce Trajcevski, Ting Zhong, and Kunpeng Zhang","Fan Zhou, &lt;b&gt;Xovee Xu&lt;/b&gt;, Kunpeng Zhang, Goce Trajcevski, and Ting Zhong*"],"citation":[0,0,0,1,0,2,0,3,0,4,0,4,63,6,4,1,6,19],"date":[1653177600000.0,1645488000000.0,1645488000000.0,1645488000000.0,1645488000000.0,1626739200000.0,1643673600000.0,1643673600000.0,1638835200000.0,1635724800000.0,1635206400000.0,1625961600000.0,1614556800000.0,1607299200000.0,1607299200000.0,1607299200000.0,1585180800000.0,1593993600000.0],"title":["Probabilistic Fine-Grained Urban Flow Inference With Normalizing Flows","Linking Transformer to Hawkes Process for Information Cascade Prediction (Student Abstract)","Conditional Collaborative Filtering Process for Top-K Recommender System (Student Abstract)","Fine-Grained Urban Flow Inference via Normalizing Flows (Student Abstract)","PrEF: Probabilistic Electricity Forecasting via Copula-Augmented State Space Model","CCGL: Contrastive Cascade Graph Learning","Heterogeneous Dynamical Academic Network for Learning Scientific Impact Propagation","Contrastive Trajectory Learning for Tour Recommendation","HGENA: A Hyperbolic Graph Embedding Approach for Network Alignment","CasFlow: Exploring Hierarchical Structures and Propagation Uncertainty for Cascade Prediction","Vector-Quantized Autoencoder With Copula for Collaborative Filtering","Decoupling Representation and Regressor for Long-Tailed Information Cascade Prediction","A Survey of Information Cascade Analysis: Models, Predictions, and Recent Advances","Continual Information Cascade Learning","Unsupervised User Identity Linkage via Graph Neural Networks","Meta-Learned User Preference for Topic Participation Prediction","A Heterogeneous Dynamical Graph Neural Networks Approach to Quantify Scientific Impact","Variational Information Diffusion for Probabilistic Cascades Prediction"],"venue":["ICASSP 22","AAAI Student Abs. 22","AAAI Student Abs. 22","AAAI Student Abs. 22","AAAI 22","TKDE 22","KBS 22","ACM TIST","GLOBECOM 21","TKDE 21","CIKM 21","SIGIR 21","CSUR 21","GLOBECOM 20","GLOEBCOM 20","GLOBECOM 20","arXiv 20","INFOCOM 20"]},"selected":{"id":"1071"},"selection_policy":{"id":"1070"}},"id":"1002","type":"ColumnDataSource"},{"attributes":{"axis":{"id":"1019"},"coordinates":null,"dimension":1,"group":null,"ticker":null},"id":"1022","type":"Grid"},{"attributes":{"fill_color":{"value":"green"}},"id":"1039","type":"Circle"},{"attributes":{},"id":"1020","type":"BasicTicker"},{"attributes":{"fill_color":{"value":"green"},"line_color":{"value":"white"},"size":{"value":10},"x":{"field":"date"},"y":{"field":"citation"}},"id":"1034","type":"Circle"},{"attributes":{"dimensions":"width"},"id":"1024","type":"WheelZoomTool"},{"attributes":{"dimensions":"width"},"id":"1023","type":"PanTool"},{"attributes":{},"id":"1025","type":"ResetTool"},{"attributes":{"active_scroll":{"id":"1024"},"logo":null,"tools":[{"id":"1023"},{"id":"1024"},{"id":"1025"},{"id":"1060"},{"id":"1061"}]},"id":"1026","type":"Toolbar"}],"root_ids":["1004"]},"title":"Bokeh Application","version":"2.4.3"}}
    </script>
    <script type="text/javascript">
      (function() {
        const fn = function() {
          Bokeh.safely(function() {
            (function(root) {
              function embed_document(root) {
              const docs_json = document.getElementById('1416').textContent;
              const render_items = [{"docid":"22f53ee2-9ac0-4bfd-a331-ae52e3411ca5","root_ids":["1004"],"roots":{"1004":"927262b8-6435-4aee-a4b8-aa6755c0f2cd"}}];
              root.Bokeh.embed.embed_items(docs_json, render_items);
              }
              if (root.Bokeh !== undefined) {
                embed_document(root);
              } else {
                let attempts = 0;
                const timer = setInterval(function(root) {
                  if (root.Bokeh !== undefined) {
                    clearInterval(timer);
                    embed_document(root);
                  } else {
                    attempts++;
                    if (attempts > 100) {
                      clearInterval(timer);
                      console.log("Bokeh: ERROR: Unable to run BokehJS code because BokehJS library is missing");
                    }
                  }
                }, 10, root)
              }
            })(window);
          });
        };
        if (document.readyState != "loading") fn();
        else document.addEventListener("DOMContentLoaded", fn);
      })();
    </script>
  </body>
</html>
```


### Stack traceback or browser console output

_No response_

### Screenshots

_No response_
user: Xovee
issue_12308: state: open
title: [FEATURE] Move compilation chain to separate package
body: ### Problem description

Currently the bokeh Python package (
bokeh-3.0.0.dev11.tar.gz) is 15.7MB compressed and the majority of this comes from two files which are actually not needed for the absolutely majority of users:

- bokeh/server/static/js/bokeh.json (22.61 MB)
- bokeh/server/static/js/compiler.js (20.6 MB)

If I remove these two files the compressed package is less than half the size at 7.7 MB. If you go further and also delete unminified `.js` files you go down to 4.4 MB.

### Feature description

What I would suggest is that we streamline the main Bokeh Python package to only include the files that end users actually need. For developers of extensions we could create a separate `bokeh-dev` package that includes the remaining files required for compiling.
user: philippjfr
issue_12303: state: open
title: [FEATURE] Improved error message on token expiry
body: ### Problem description


Bokeh server can be used to serve notebooks (e.g. with panel) which often have slow data preprocessing steps to execute before anything visual can be served. When the notebook takes too long to run, the token expires and the user will see a message such as:

```
2022-08-11 15:16:32,894 Stripping out IPython magic %%time in code cell 4
2022-08-11 15:16:32,895 Stripping out IPython magic %%time in code cell 7
2022-08-11 15:16:32,895 Stripping out IPython magic %%time in code cell 8
2022-08-11 15:16:33,172 Starting Bokeh server version 2.2.3 (running on Tornado 6.1)
2022-08-11 15:16:33,174 User authentication hooks NOT provided (default user enabled)
2022-08-11 15:16:33,177 Bokeh app running at: http://localhost:5006/Notebook_Example
2022-08-11 15:16:33,178 Starting Bokeh server with process id: 84423
tornado.application - ERROR - Uncaught exception GET /Notebook_Example/ws (::1)
HTTPServerRequest(protocol='http', host='localhost:5006', method='GET', uri='/Notebook_Example/ws', version='HTTP/1.1', remote_ip='::1')
Traceback (most recent call last):
  File "/Users/jstevens/miniconda3/envs/vault/lib/python3.7/site-packages/tornado/websocket.py", line 954, in _accept_connection
    open_result = handler.open(*handler.open_args, **handler.open_kwargs)
  File "/Users/jstevens/miniconda3/envs/vault/lib/python3.7/site-packages/tornado/web.py", line 3173, in wrapper
    return method(self, *args, **kwargs)
  File "/Users/jstevens/miniconda3/envs/vault/lib/python3.7/site-packages/bokeh/server/views/ws.py", line 137, in open
    raise ProtocolError("Token is expired.")
bokeh.protocol.exceptions.ProtocolError: Token is expired.
```

This traceback doesn't make clear what can be done to avoid this error.

### Feature description

It would be nice to catch this error or augment this traceback with a message to the user to use the `--session-token-expiration` option which a larger timeout (ideally, stating the current timeout value)

### Potential alternatives

Increasing the default value of the timeout would help but is probably not a desirable solution due to security considerations.

### Additional information

_No response_
user: jlstevens
issue_12297: state: open
title: [BUG] vbar_stack is only updated once by CustomJS
body: ### Software versions

Python version      :  3.9.10 | packaged by conda-forge | (main, Feb  1 2022, 21:24:11) 
IPython version     :  8.4.0
Tornado version     :  6.1
Bokeh version       :  2.4.3
BokehJS static path :  /opt/conda/envs/aiva-env/lib/python3.9/site-packages/bokeh/server/static
node.js version     :  v17.4.0
npm version         :  8.3.1
Operating system    :  Linux-4.19.0-20-amd64-x86_64-with-glibc2.31

### Browser name and version

Google Chrome Version 104.0.5112.80

### Jupyter notebook / Jupyter Lab version

Selected Jupyter core packages... IPython          : 8.4.0 ipykernel        : 6.9.1 ipywidgets       : 7.7.1 jupyter_client   : 7.1.2 jupyter_core     : 4.9.2 jupyter_server   : not installed jupyterlab       : not installed nbclient         : 0.5.12 nbconvert        : 6.4.2 nbformat         : 5.1.3 notebook         : 6.4.8 qtconsole        : not installed traitlets        : 5.1.1

### Expected behavior

I have a CustomJS block and a Slider and I expect, that the figure is updated every time the slider is moved.

### Observed behavior

As you can see in the gif below, the slider is moved but the figure is only updated once.

![vstack](https://user-images.githubusercontent.com/68053396/183456819-2b979a1b-5c4a-4990-9ea0-0e5bbb146d6f.gif)

To make sure the Source is updated, I printed the updated values to the console, which works fine.

### Example code

```Python
from bokeh.plotting import figure, show
from bokeh.models import CustomJS,ColumnDataSource, Slider
from bokeh.layouts import column
from bokeh.palettes import GnBu3

years = ['2015', '2016', '2017']
fruits = ['Apples', 'Pears', 'Nectarines', 'Plums', 'Grapes', 'Strawberries']
exports = {'fruits' : fruits,
           '2015'   : [2, 1, 4, 3, 2, 4],
           '2015_org'   : [2, 1, 4, 3, 2, 4],
           '2016'   : [5, 3, 4, 2, 4, 6],
           '2017'   : [3, 2, 4, 4, 5, 3]
          }

source = ColumnDataSource(exports)

p = figure(x_range=fruits, height=250, y_range=(0, 16), title="Fruit import/export, by year")

p.vbar_stack(years, x='fruits', width=0.9, color=GnBu3, source=source)

p.x_range.range_padding = 0.1
p.xgrid.grid_line_color = None

slider = Slider(start=0, end=5, value=0, step=1, title="2015 exports")

callback = CustomJS(
    args=dict(source=source, slider=slider),
    code="""
    console.log(slider.value)
    let y = source.data['2015'];
    let y_org = source.data['2015_org']
    for (let i = 0; i < y.length; i++) {
        y[i] = y_org[i] + slider.value
    }
    console.log(y)
    source.change.emit();
""")

slider.js_on_change('value', callback)

show(column(p,slider))
```

gives the wanted results. But the other version should workd and was working in other examples for me.
user: mosc9575
issue_12288: state: open
title: Refine tooltip API
body: As discussed with @mattpap in https://github.com/bokeh/bokeh/pull/12242, there are a few tooltip-related API refinements to finish before the next release.

One example that has come up is the Tooltip's `position` property (default value) https://github.com/bokeh/bokeh/pull/12242#issuecomment-1200342335, another is the InputWidget's `description` property (https://github.com/bokeh/bokeh/pull/12242#issuecomment-1193060156).
user: tcmetzger
issue_12282: state: open
title: Icons in documentation need to be updated after PR #12277
body: Note we still use PNG icons for some more obscure cases (e.g. edit tools). SVG variants need to be created for those. SVG icons are now stored in bokehjs' `src/less/icons`, as opposed to PNG icons which are stored in the docs. Also note that the new icons are designed to be used as masks to create actual images, thus they don't contain explicit colors (they use `currentColor`) and are embedded using CSS' `mask-*` properties instead of `background-*`.

/cc @tcmetzger 
user: mattpap
issue_12266: state: open
title: `test_js_license_set` is bogus and unreliable
body: After changes to bokehjs' build setup, `package.json` files, etc., `license-checker` returns bogus results in production mode, which the test uses:
```
~/repo/bokeh/bokehjs$ npx license-checker --summary --production
└─ BSD-3-Clause: 1
```
Otherwise, it returns a lot of licenses that aren't present in bokehjs' bundles:
```
~/repo/bokeh/bokehjs$ npx license-checker --summary
├─ MIT: 265
├─ ISC: 28
├─ BSD-3-Clause: 15
├─ BSD-2-Clause: 12
├─ Apache-2.0: 11
├─ UNLICENSED: 5
├─ 0BSD: 2
├─ (Unlicense OR Apache-2.0): 1
├─ Python-2.0: 1
├─ (MIT OR Apache-2.0): 1
├─ (MIT OR CC0-1.0): 1
└─ MIT*: 1
```
Additionally, it reports different licenses in CI and locally.

I disabled this test in PR #12219, to allow to merge this PR. We will have to figure out a more robust way of checking licenses. Also, minified bundles are stripped of licences, whereas unminified bundles contain them.
user: mattpap
issue_12262: state: open
title: Calendar example fails to build in docs
body: `make html` fails with:
```
bokeh-plot:: error:

Traceback (most recent call last):
  File "/home/mateusz/repo/bokeh/bokeh/application/handlers/code_runner.py", line 231, in run
    exec(self._code, module.__dict__)
  File "/home/mateusz/repo/bokeh/examples/models/file/calendars.py", line 96, in <module>
    months = [ [ make_calendar(2014, 3*i + j + 1) for j in range(3) ] for i in range(4) ]
  File "/home/mateusz/repo/bokeh/examples/models/file/calendars.py", line 96, in <listcomp>
    months = [ [ make_calendar(2014, 3*i + j + 1) for j in range(3) ] for i in range(4) ]
  File "/home/mateusz/repo/bokeh/examples/models/file/calendars.py", line 96, in <listcomp>
    months = [ [ make_calendar(2014, 3*i + j + 1) for j in range(3) ] for i in range(4) ]
  File "/home/mateusz/repo/bokeh/examples/models/file/calendars.py", line 28, in make_calendar
    firstweekday = list(day_abbrs).index(firstweekday)
ValueError: 'Mon' is not in list
```
but I can run this example manually and it works fine. All dependencies are up-to-date.
user: mattpap
issue_12259: state: open
title: [FEATURE] Support for accessibility tags
body: ### Problem description

Bokeh makes it easy to build web apps, but as far as I can see, there isn't an easy way to make them accessible for screen readers or other non-graphical interaction or inspection.

### Feature description

 It would be great if some low-level base class in Bokeh would support some of the [ARIA](https://www.w3.org/TR/html-aria) tags so that an app author could easily supply human-readable text describing what a given plot contains or what a given layout contains or what a given control does. 

### Potential alternatives

There's a bit of overlap with the [recent tooltips work](https://github.com/bokeh/bokeh/pull/12154), but I think this is really a separate request.

### Additional information

_No response_
user: jbednar
issue_12227: state: open
title: [BUG] IndexFilter + Stream, renderer update not behaving as expected
body: ### Software versions

Python version      :  3.7.12 | packaged by conda-forge | (default, Oct 26 2021, 05:37:49) [MSC v.1916 64 bit (AMD64)]
IPython version     :  7.33.0
Tornado version     :  6.1
Bokeh version       :  2.4.3
BokehJS static path :  C:\Users\Gaelen\.conda\envs\geo_env37v3\lib\site-packages\bokeh\server\static
node.js version     :  v17.9.0
npm version         :  8.5.5
Operating system    :  Windows-10-10.0.19041-SP0

### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

I have two CDSs (s1 and s2), related by an id field in both of them. The idea is to use CustomJS coupled with an IndexFilter to allow the user to tap-select a single index in s1 and filter s2 for only the selected id in s2. This facilitates essentially a one-to-many filter action. I’m not using CustomJSFilter intentionally because in my actual use case there’s more going on than just this.

So this one to many IndexFilter approach works fine:

![works1](https://user-images.githubusercontent.com/58000181/178272073-547132d7-e3fd-400d-8440-c29b7f8dfaba.gif)

The next thing I’m trying to do is append (i.e. stream) data to s2 via CustomJS. For the MRE below I’ve made a button and a CustomJS instance that will retrieve the current maximum x-y value in s2 and add one to both for the next data point (so the first button click will add an ‘apple’ at 101,101, next button click will add another apple at 102,102 etc. With the CDSView removed from the s2 renderer, this also works as intended:

![works2](https://user-images.githubusercontent.com/58000181/178272119-0c8a7525-3e6e-4515-9a32-f4613092ecc0.gif)



### Observed behavior

Now the combination of these two features is what’s not behaving the way I expected. When the user selects an apple from s1 (i.e. filters down to just apples on s2), then attempts to add an apple, I want the filter indices to update accordingly. So in my MRE, I show that you can accomplish this two different ways → execute the filter callback in the stream callback (probably pretty inefficient and unnecessary) or check if the currently selected thing is an apple, and if so, add the new index to the filter. In either case, based on the console.log, s2.data’s value arrays grow in length by one, and the current filter indices are reporting that the new value’s index is added:

![works3](https://user-images.githubusercontent.com/58000181/178272181-a07ae850-9daf-48a0-a42f-88bc54761e6d.gif)

BUT when the button is pressed, the new point does not render!

![works4](https://user-images.githubusercontent.com/58000181/178272234-4bf2782f-a867-4b05-ad06-94343e88339f.gif)

^^should be displaying a new red point at 101,101 but doesn’t!^^

Now where it gets even stranger is if I click the button twice (or more than twice):

![works5](https://user-images.githubusercontent.com/58000181/178272746-2a956d2d-f774-471c-9217-5ad37ee57556.gif)

What’s happening as far as I can tell, is that the renderer is always one update behind the callback → click once, nothing shows up, click twice, what should have shown up on the first click now shows up but what should show up on the second click doesn’t etc. etc.




Finally, If I click the button once (and the new datapoint doesn’t render), then change the selected indices of s1), then go back to that selection, the new datapoint actually shows up:

![works6](https://user-images.githubusercontent.com/58000181/178272648-c6b4204d-f410-428b-9b1e-405d4c30648b.gif)


Which leads me to my current workaround → in the stream callback, store the currently selected s1 indices in a dummy variable, set s1.selected.indices to an empty array, then set it back to the dummy variable. And that works:

![works7](https://user-images.githubusercontent.com/58000181/178272690-010e10e8-345c-463f-bef5-6a17ad11aca6.gif)

Via discourse, I got another workaround from @bryevdv : push the new index to the filter PRIOR to streaming the data:

```
add_cb = CustomJS(args=dict(s1=s1, s2=s2, filt=filt), code="""
    const nx = Math.max(...s2.data.x) + 1
    const ny = Math.max(...s2.data.y) + 1
    const new_data = {x: [nx], y: [ny], id: ['apple'], c: ['red']}
    const id = s1.data.id[s1.selected.indices[0]]
    if (id == 'apple') {
        filt.indices.push(s2.data.id.length)  # remove the -1
    }
    s2.stream(new_data)
""")
```

This feels pretty janky given the index doesn't exist but you're adding it to the filter in anticipation of it existing.

One other thing I will mention that I've noticed is that if you inspect the view after streaming the new datapoint and updating the indices, is that the _indices property updates to have the new datapoint, but drilling into the indices property/object its "count" property does not:

![image](https://user-images.githubusercontent.com/58000181/178276706-9a3718a9-8ff1-4127-a058-b1a16f6c8ade.png)

 ^^ see how _indices here is 42 long while indices._count is still only 41. This may have something to do with it but I'm not sure.

Relevant Discourse post here: https://discourse.bokeh.org/t/filtering-cdsviewing-streaming/9359



### Example code

```Python
# -*- coding: utf-8 -*-
"""
Created on Thu Jul  7 14:51:41 2022

@author: Gaelen
"""

import numpy as np
import pandas as pd
from bokeh.plotting import figure, save
from bokeh.models import MultiLine, IndexFilter, CDSView, ColumnDataSource,Scatter,CustomJS, Button
from bokeh.layouts import layout

fdict = {'banana':'yellow','apple':'red','orange':'orange'}
d1 = {'x':np.arange(0,3,1),'y':np.arange(0,3,1),'id':list(fdict.keys()),'c':list(fdict.values())}
n = 101
d2 = {'x':np.arange(1,n,1),'y':np.arange(1,n,1),'id':np.random.choice(list(fdict.keys()),n-1)}
d2['c'] = [fdict[f] for f in d2['id']]

s1 = ColumnDataSource(d1)
s2 = ColumnDataSource(d2) 

#first figure, user to make a selection to update the filter
f1 = figure(tools=['wheel_zoom','tap'],width=400,height=400)
f1_glyph = Scatter(x='x',y='y',size=25,fill_color='c')
f1_rend = f1.add_glyph(s1,f1_glyph)
#second figure, user to view the corresponding fruits for the one selected in the other source
f2 = figure(width=400,height=400,x_range=(0,105),y_range=(0,105))
f2_glyph = Scatter(x='x',y='y',size=10,fill_color='c')

#make the IndexFilter and the callback to update the filter's indices 
filt = IndexFilter(indices=[])
filt_cb = CustomJS(args=dict(s1=s1,s2=s2,filt=filt)
              ,code='''
              var sel_ind = s1.selected.indices[0]
              var sel_fruit = s1.data['id'][sel_ind]
              var filt_inds = []
              for (var i = 0; i<s2.data['id'].length; i++){
                      if (s2.data['id'][i]==sel_fruit){
                              filt_inds.push(i)}
                      }
              filt.indices = filt_inds
              s2.change.emit()
              ''')
view = CDSView(source=s2,filters=[filt])

f2_rend = f2.add_glyph(s2,f2_glyph
                        ,view=view
                       )
s1.selected.js_on_change('indices',filt_cb) 


btn = Button(label='Add_Apple')

add_cb = CustomJS(args=dict(s2=s2,filt=filt,s1=s1,filt_cb=filt_cb,view=view)
                  ,code='''
                  //get the new data to stream
                  var nx = Math.max(...s2.data['x'])+1
                  var ny = Math.max(...s2.data['y'])+1
                  var new_data = {'x':[nx],'y':[ny], 'id':['apple'],'c':['red']}
                  s2.stream(new_data)
                  console.log(s2.data)
                  //two ways to update the filter indices
                  //1 simple but probably slow, run the filt_cb
                  //filt_cb.execute()
                  //2 check the selected index is an apple and if so just push the new index to filt.indices
                  var sel_id = s1.data['id'][s1.selected.indices[0]]
                  console.log(sel_id)
                  if ( sel_id == 'apple'){
                          filt.indices.push(s2.data['id'].length-1)
                          }
                  view.properties.filters.change.emit() //kinda grasping at straws emitting changes etc.
                  s2.change.emit() //kinda grasping at straws emitting changes etc.

                  console.log(view._indices) // take a look at what the view's indices are reporting after all this'
                  
                  // my workaround
                  //var si = s1.selected.indices
                  //s1.selected.indices=[]
                  //s1.selected.indices=si
                  '''
                  )


btn.js_on_click(add_cb)


lo = layout([[f1,f2],btn])
save(lo,'filt_bug_testing.html')
```


### Stack traceback or browser console output

_No response_

### Screenshots

_No response_
user: gmerritt123
issue_12215: state: open
title: Allow relative placement of renderers in dynamically sized parents
body: ### Problem description

The current way of specifying positions of labels doesn't allow you to position them relative to the borders of dynamically sized figures in other locations than the lower left corner. I've previously brought this issue up in the Discourse forum: https://discourse.bokeh.org/t/placement-of-label-relative-to-figure/9349

It's very simple to create a label that stays put in the lower left corner even when zooming and panning this dynamically sized figure:

```python
from bokeh.plotting import figure, show
from bokeh.models import Label

fig = figure(title='Label position test')
fig.sizing_mode = 'stretch_both'

fig.circle([1, 2, 3, 4, 5], [6, 7, 2, 4, 5], size=20, color="navy", alpha=0.5)

lower_left = Label(x=5, y=5, x_units='screen', y_units='screen', text='Lower left corner')
fig.add_layout(lower_left)

show(fig)
```

![Screenshot from 2022-07-05 11-25-58](https://user-images.githubusercontent.com/33153877/177296577-96c41460-5d2a-4489-bcec-297133776621.png)

However, if I want to place a label in any of the other corners there's no way for me to do this reliably, because I don't know the size of the figure beforehand.

### Feature description

An intuitive way of handling this would be to allow for negative values to offset the labels from the opposite side of the figure. Negative values are supported as coordinates, but they don't function in the way I described:

```python
from bokeh.plotting import figure, show
from bokeh.models import Label

fig = figure(title='Label position test')
fig.sizing_mode = 'stretch_both'

fig.circle([1, 2, 3, 4, 5], [6, 7, 2, 4, 5], size=20, color="navy", alpha=0.5)

lower_left = Label(x=5, y=5, x_units='screen', y_units='screen', text='Lower left corner')
fig.add_layout(lower_left)

# How can I position this relative to the upper bound of the figure?
upper_left = Label(x=5, y=-5, x_units='screen', y_units='screen', text='Upper left corner')
fig.add_layout(upper_left)

show(fig)
```

![Screenshot from 2022-07-05 11-31-53](https://user-images.githubusercontent.com/33153877/177297827-6720db9a-90d2-40bf-a52c-8652f27caef6.png)


### Potential alternatives

For the specific example of a label in the upper left corner @bryevdv suggested doing a range callback and updating the y-position of the label whenever the range changes.

### Additional information

_No response_
user: huaracheguarache
issue_12187: state: open
title: [BUG] Dates displayed as zero epoch in a DataTable is one element isn't a date
body: ### Software versions

Python version      :  3.8.12 (default, Oct 12 2021, 06:23:56) 
IPython version     :  7.30.0
Tornado version     :  6.1
Bokeh version       :  2.4.3
BokehJS static path :  /Users/mliquet/miniconda3/envs/panel-dev38/lib/python3.8/site-packages/bokeh/server/static
node.js version     :  v17.0.1
npm version         :  8.1.0
Operating system    :  macOS-10.16-x86_64-i386-64bit

### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

The `DateFormatter` should format the dates even when the column contains NaNs.

### Observed behavior

If the column contains NaNs the non-NaN dates are all rendered as zero epoch (1970-01-01). Actually, it's not just NaNs, if in the example I replace `np.nan` by `None`, or even `"foo"`, the table is always rendered in the same way, as shown in the screenshot. This bug looks similar to this one: https://github.com/bokeh/bokeh/issues/10769

### Example code

```Python
import numpy as np

from datetime import date
from random import randint

from bokeh.io import show
from bokeh.models import ColumnDataSource, DataTable, DateFormatter, TableColumn

import pandas as pd

data = pd.DataFrame(dict(
        dates=[date(2014, 3, i+1) for i in range(10)],
        downloads=[randint(0, 100) for i in range(10)],
))
data.iloc[0, 0] = np.nan
source = ColumnDataSource(data)

columns = [
        TableColumn(field="dates", title="Date", formatter=DateFormatter()),
        TableColumn(field="downloads", title="Downloads"),
    ]
data_table = DataTable(source=source, columns=columns, width=400, height=280)

show(data_table)
```


### Stack traceback or browser console output

_No response_

### Screenshots

![image](https://user-images.githubusercontent.com/35924738/174913359-64874d24-70d8-40e4-9850-fcbfa0f92021.png)

user: maximlt
issue_12185: state: open
title: [FEATURE] Legend click events
body: ### Problem description

When plotting a figure you can create legends that enable you to either hide or mute individual glyphs. This is one simple example with hiding functionality:

```
from bokeh.plotting import figure, show
import numpy as np

x = np.linspace(0, 2*np.pi, 101)

fig = figure()
fig.line(x, np.cos(x), color="red", legend_label="cos(x)")
fig.line(x, np.sin(x), color="green", legend_label="sin(x)")
fig.line(x, np.cos(x) + 1, color="blue", legend_label="cos(x) + 1")

fig.legend.location = "right"
fig.legend.click_policy = "hide"

show(fig)
```

In the example above there are only 3 glyphs that can be hidden, but say you have a plot with more than 20 glyphs and you want to have only one visible. It would be quite tedious to individually have to hide each glyph by clicking its legend.

### Feature description

The solution to this issue could be to provide a way to customise the functionality of legends beyond the hide and mute options already available. I would like it to be possible to create an additional custom legend that can hide/show all glyphs when clicked.

### Potential alternatives

I previously [asked on the Bokeh Discourse forum](https://discourse.bokeh.org/t/is-there-a-way-to-hide-show-all-glyphs-with-one-action/9310) whether it's possible to create a custom legend with the ability to hide/show all glyphs, and I was provided with an alternative solution using CustomJS and a button:

```
from bokeh.plotting import figure, show
from bokeh.models import CustomJS, Button
from bokeh.layouts import row
import numpy as np

from bokeh.models import Button

x = np.linspace(0, 2*np.pi, 101)

fig = figure()
fig.line(x, np.cos(x), color="red", legend_label="cos(x)")
fig.line(x, np.sin(x), color="green", legend_label="sin(x)")
fig.line(x, np.cos(x) + 1, color="blue", legend_label="cos(x) + 1")

fig.legend.location = "right"
fig.legend.click_policy = "hide"

btn = Button(label='Hide All')
cb = CustomJS(args=dict(fig=fig,btn=btn)
              ,code='''
              if (btn.label=='Hide All'){
                  for (var i=0; i<fig.renderers.length; i++){
                          fig.renderers[i].visible=false}
                  btn.label = 'Show All'
                  }
              else {for (var i=0; i<fig.renderers.length; i++){
                      fig.renderers[i].visible=true}
              btn.label = 'Hide All'}
              ''')

btn.js_on_click(cb)
show(row([fig,btn]))
```

It's a good workaround, but it would be more natural for the functionality to be part of a custom legend in my opinion.

### Additional information

_No response_
user: huaracheguarache
issue_12175: state: open
title: [BUG] All Tabs panels refreshed when adding/closing a panel
body: ### Software versions

Python version      :  3.8.10
IPython version     :  8.2.0
Tornado version     :  6.1
Bokeh version       :  2.4.3
BokehJS static path :  /home/mateusz/PycharmProjects/stock-chance/venv/lib/python3.8/site-packages/bokeh/server/static
node.js version     :  v14.18.3
npm version         :  6.14.15
Operating system    :  Linux-5.13.0-44-generic-x86_64-with-glibc2.29

### Browser name and version

Not related

### Jupyter notebook / Jupyter Lab version

Not related

### Expected behavior

Existing tabs should not be refreshed/repainted when dynamically adding a new Tab from Python code or closing a Tab in browser.

### Observed behavior

Steps to reproduce:
1. Run below code
2. Sort table in descending order by clicking on the 'Num' header **twice**
3. Click on the 'Add tab' button
4. The ordering of numbers in the first tab changes to the initial state, because the first tab was refreshed

The same behavior is observed when one tab is closed by clicking on the 'x'. Also tested on multiple open tabs - all of them are refreshed/repainted when we add/close a tab. The issue is significantly slowing down dynamic addition of new tabs when complex plots are presented on them.

### Example code

```Python
from bokeh.io import curdoc
from bokeh.layouts import column
from bokeh.models import ColumnDataSource, Tabs, Panel, DataTable, TableColumn, Button


def create_tab():
    source = ColumnDataSource({'num': list(range(10))})
    table = DataTable(columns=[TableColumn(field='num', title='Num')], source=source)
    return Panel(child=table, title='Tab', closable=True)


tabs = Tabs(tabs=[create_tab()])
button = Button(label='Add tab')
button.on_click(lambda _: tabs.tabs.append(create_tab()))

curdoc().add_root(column(button, tabs))
```


### Stack traceback or browser console output

_No response_

### Screenshots

_No response_
user: mgawino
issue_12172: state: open
title: [BUG] StringFormatter doesn't apply nan_format
body: ### Software versions

Python version      :  3.7.12 | packaged by conda-forge | (default, Oct 26 2021, 06:08:21) 
IPython version     :  7.33.0
Tornado version     :  6.1
Bokeh version       :  2.4.3
BokehJS static path :  /home/liquetm/miniconda3/envs/panel-dev37/lib/python3.7/site-packages/bokeh/server/static
node.js version     :  v17.9.0
npm version         :  8.5.5
Operating system    :  Linux-5.4.0-117-generic-x86_64-with-debian-buster-sid

### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

The [documentation](https://docs.bokeh.org/en/latest/docs/reference/models/widgets.tables.html#bokeh.models.widgets.tables.ScientificFormatter.nan_format) of the `StringFormatter` indicates that `nan_format` can be used to format `None` and `NaN` values in a column. However as shown in the example and the screenshot this doesn't seem to be applied at all.

### Observed behavior

It seems like the `StringFormatter` code doesn't handle `nan_format` at all, but I may have missed another place where the formatter is handled:

https://github.com/bokeh/bokeh/blob/423ba41ea57c08cb37021e8ed5a0fb6807c6e9b2/bokehjs/src/lib/models/widgets/tables/cell_formatters.ts#L69

Happy to work on a PR if this is an oversight.

### Example code

```Python
from bokeh.plotting import curdoc

from bokeh.models import ColumnDataSource, DataTable, TableColumn, StringFormatter
import numpy as np

s = ['a'] * 5
s1 = s.copy()
s1[0] = None
s2 = s.copy()
s2[0] = np.nan

data = dict(s1=s1, s2=s2)
source = ColumnDataSource(data)

columns = [
        TableColumn(field="s1", formatter=StringFormatter(text_color='red', nan_format='-')),
        TableColumn(field="s2", formatter=StringFormatter(text_color='red', nan_format='-')),
    ]
data_table = DataTable(source=source, columns=columns, width=400, height=280)

curdoc().add_root(data_table)
```


### Stack traceback or browser console output

_No response_

### Screenshots

![image](https://user-images.githubusercontent.com/35924738/173511361-40720a80-458d-4594-981a-bccee0ecd41d.png)

user: maximlt
issue_12160: state: open
title: [BUG] theme is not applied to json_item plot
body: ### Software versions

Python version      :  3.9.12 (main, Mar 26 2022, 15:52:10)
IPython version     :  8.4.0
Tornado version     :  6.1
Bokeh version       :  2.4.3
BokehJS static path :  /Users/jlc/ds/myenv/lib/python3.9/site-packages/bokeh/server/static
node.js version     :  v18.0.0
npm version         :  8.6.0
Operating system    :  macOS-11.6.6-x86_64-i386-64bit

### Browser name and version

tried Safari and Firefox - page looks the same either way

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

I expected the file dark.html to have a dark plot, the file light.html to have a light plot

### Observed behavior

both plots are light

### Example code

```Python
import json
from pathlib import Path
from typing import Dict, Any

from bokeh.plotting import figure
from bokeh.embed import json_item
from bokeh.resources import CDN
from bokeh.themes import LIGHT_MINIMAL, DARK_MINIMAL
from bokeh.models import ColumnDataSource


def really_simple_template(title, css, js, plot):

    return f"""
<!DOCTYPE html>
<html lang="en">

<head>
    <link rel="stylesheet" href="https://unpkg.com/mvp.css">

    {css}
    {js}

    <meta charset="utf-8">
    <meta name="description" content="My description">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <script>
    function insertPlot() {{
        Bokeh.embed.embed_item({plot}, "a_spot_for_my_plot")
    }}
    window.onload = insertPlot;
    </script>
</head>

<body>
    <header>
	    <h2>{title}</h2>
    </header>
    <main>
	    <div id="a_spot_for_my_plot"></div>
    </main>
    <footer>
    </footer>
</body>
</html>
    """

def dummy_plot(dark_mode: bool) -> Dict[str, Any]:

    xs = [1, 2, 3, 4, 5, 6]
    ys = [3, 6, 5, 4, 7, 1]
    data = ColumnDataSource({"x": xs, "y": ys})

    p = figure(title="Simple Plot Example", x_axis_label="x", y_axis_label="y")
    line = p.line(x="x", y="y", source=data, legend_label="A line!", line_width=2)

    if dark_mode:
        theme = DARK_MINIMAL
    else:
        theme = LIGHT_MINIMAL

    plot_json = json_item(p, target=None, theme=theme)
    plot_string = json.dumps(plot_json)

    return {
        "bokeh_css": CDN.render_css(),
        "bokeh_js": CDN.render_js(),
        "bokeh_plot": plot_string,
    }

def main():
    dark = dummy_plot(True)
    dark_html = really_simple_template(
        "Dark plot", dark["bokeh_css"], dark["bokeh_js"], dark["bokeh_plot"]
    )
    Path("dark.html").write_text(dark_html)

    light = dummy_plot(False)
    light_html = really_simple_template(
        "Light plot", light["bokeh_css"], light["bokeh_js"], light["bokeh_plot"]
    )
    Path("light.html").write_text(light_html)


if __name__ == "__main__":
    main()
```


### Stack traceback or browser console output

_No response_

### Screenshots

_No response_
user: j-carson
issue_12129: state: open
title: [BUG] CDS updates on figure data that change the axis magnitude will trigger a recomputation of the complete layout
body: ### Software versions

Python version      :  3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)]
IPython version     :  (not installed)
Tornado version     :  6.1
Bokeh version       :  3.0.0dev2

### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

Data updates on plots via CDS should not unnecessarily invalidate the complete layout but instead only adjust the figure that is updated.

### Observed behavior

Data updates that cause the magnitude of the y-axis to change (i.e. from single digits to double digits or the other way round) will invalidate the complete layout and repaint it from scratch again. This seems rather unnecessary and is a huge performance bottleneck. 

This can be reproduced with my example below: here we have 50 figures and tables respectively with only the first plot table combination being updated via the buttons on the left. Both buttons will trigger random data updates via CDS but scale differently (< 10 and < 100). Everytime magnitudes change the repainting of the layout is triggered which takes around **1 seconds** to complete (see screenshot below, updates without magnitude changes work nearly instantly). If instead all 50 plots are updated this behaviour scales linearly (**50 seconds** to update) which is rather undesired.

As a side note: embedding the layout within a tab widget makes things even worse as the performance is decreased by a factor of 2.

### Example code

```Python
import numpy as np
import pandas as pd

from bokeh.io import output_notebook, curdoc

from bokeh.layouts import row, layout, column
from bokeh.layouts import grid
from bokeh.models import CustomJS, ColumnDataSource, Panel, Tabs, PreText, Div, Legend, FactorRange, RadioButtonGroup
from bokeh.models import CheckboxButtonGroup
from bokeh.models import Button  # for saving data
from bokeh.models.widgets import DataTable, DateFormatter, TableColumn
from bokeh.models import HoverTool
from bokeh.plotting import figure

from bokeh.models import ColumnDataSource, CustomJS
from bokeh.models.widgets import Button

doc = curdoc()


class ViewModelDummy:
    n_rows = 8
    n_cols = 8

    def __init__(self):
        self.cds_table = ColumnDataSource({})
        self.cds_plot = ColumnDataSource({})
        self.plot_view = figure(title="Basic Title", width=400, height=300,
                                x_range=FactorRange(*self.get_columns(ViewModelDummy.n_cols)), toolbar_location=None)
        self.table_view = DataTable(width=400, height=120, sortable=False, autosize_mode="none", source=self.cds_table,
                                    columns=self.get_table_cols(ViewModelDummy.n_cols))

        self.plot_glyph = None

    def init(self):
        self.init_data()
        self.plot_glyph = self.plot_view.vbar_stack(self.get_index(ViewModelDummy.n_rows), x='index', width=0.9,
                                                    alpha=0.5,
                                                    source=self.cds_plot, )

        self.plot_view.x_range.range_padding = 0.1
        self.plot_view.xaxis.major_label_orientation = 1
        self.plot_view.xgrid.grid_line_color = None
        return self

    def init_data(self):
        self.cds_table.data = ColumnDataSource.from_df(self.get_df())
        self.cds_plot.data = ColumnDataSource.from_df(self.get_df().transpose().reset_index(drop=False))

    def update_data(self, magnitude):
        df = self.get_df(magnitude)
        df_t = df.transpose().reset_index(drop=False)
        self.cds_plot.data = ColumnDataSource.from_df(df_t)

    def get_layout(self):
        return column(self.plot_view, self.table_view)

    def get_df(self, offset=1):
        data = np.random.rand(ViewModelDummy.n_rows, ViewModelDummy.n_cols)
        data[0,:] = offset #init first row with offset to ensure scaling is working as intended
        return pd.DataFrame(data,
                            columns=self.get_columns(ViewModelDummy.n_cols),
                            index=self.get_index(ViewModelDummy.n_rows))

    def get_columns(self, n_cols):
        return ["col" + str(i) for i in range(n_cols)]

    def get_index(self, n_rows):
        return [str(i + 10) for i in range(n_rows)]

    def get_table_cols(self, n_cols):
        return [TableColumn(field=col, title=col, width=100) for col in self.get_columns(n_cols)]


class ViewModelManager:
    def __init__(self, view_models):
        self.view_models = view_models
        self.button1_update_data = Button(label="Update Data Magnitude 1")
        self.button2_update_data = Button(label="Update Data Magnitude 10")

    def init(self):
        self.button1_update_data.on_click(self.update_data_magnitude_10)
        self.button2_update_data.on_click(self.update_data_magnitude_100)

        for vm in self.view_models:
            vm.init()

    def update_data_magnitude_10(self):
        self.view_models[0].update_data(1)  # update only first plot
        # update all plots
        # for vm in self.view_models:
        #    vm.update_data(0)

    def update_data_magnitude_100(self):
        self.view_models[0].update_data(10)  # update only first plot
        # update all plots
        # for vm in self.view_models:
        #    vm.update_data(10)

    def get_layout(self):
        n_cols_data = 2

        layout_col_left = column(self.button1_update_data, self.button2_update_data)

        layout_sub_columns_right = [[self.view_models[i].get_layout() for i in range(len(self.view_models)) if
                                     i % n_cols_data == j] for j in range(n_cols_data)]

        layout_col_right = column(row([column(col_d) for col_d in layout_sub_columns_right]))

        return row(layout_col_left, layout_col_right)


n_vm = 50
view_models = [ViewModelDummy() for i in range(n_vm)]
view_manager = ViewModelManager(view_models)
view_manager.init()

doc.add_root(view_manager.get_layout())
```


### Stack traceback or browser console output

_No response_

### Screenshots

![bokeh_layout_invalidation](https://user-images.githubusercontent.com/71090451/168450842-1bf4d66e-eb11-4599-9375-4b33a20eefda.PNG)

user: muendlein
issue_12118: state: open
title: [FEATURE] Add utilities for working in Pyodide
body: ### Problem description

With the recent announcement of [pyscript](https://github.com/Anaconda/pyscript) and jupyterlite there is now a ton of buzz around WASM Python builds, specifically pyodide. However the code required to get Bokeh to a) render in pyodide and b) bi-directionally sync Python with Javascript is not totally straightforward and some utilities would aid a user.

### Feature description

In recent releases of Panel I've started incorporating utilities for displaying panel and bokeh components in the DOM and bi-directionally sync the Python and Javascript Document instances. That code now lives in https://github.com/holoviz/panel/blob/master/panel/io/pyodide.py but most of it is not specific to Panel at all and could live in Bokeh. I've also started working on a `panel convert` utility that takes a Panel/Bokeh script and exports it to an HTML file using pyodide or pyscript. It would be great to offer some or all of these utilities directly to Bokeh users.

### Potential alternatives

One issue is that pyodide is just one particular implementation of a WASM Python build. While for now it is the default implementation there are efforts to get CPython to compile directly to WASM and these approaches may eventually replace Pyodide. For now though pyodide is the de-facto standard and is the only solution that provides convenient APIs installing packages, interacting with the DOM and some limited network access.

### Additional information

_No response_
user: philippjfr
issue_12117: state: open
title: [FEATURE] Allow BoxAnnotations in Legends
body: ### Problem description

Sometimes I use BoxAnnotations to add additional information to a plot and I would like to have the opportunity to toggle visibility using a legend in the same way it is possible for Glyphs.

This is a slightly adapted version of the [box_annotation example](https://docs.bokeh.org/en/latest/docs/gallery/box_annotation.html).

```python
from bokeh.models import BoxAnnotation, Legend, LegendItem
from bokeh.plotting import figure, show
from bokeh.sampledata.glucose import data

data = data.loc['2010-10-04':'2010-10-04']

p = figure(title="Glocose Readings, Oct 4th\n(Red = Outside Range)",
           x_axis_type="datetime", tools="pan,wheel_zoom,box_zoom,reset,save")
p.background_fill_color = "#efefef"
p.xgrid.grid_line_color=None
p.xaxis.axis_label = 'Time'
p.yaxis.axis_label = 'Value'

p.line(data.index, data.glucose, line_color='grey')
p.scatter(data.index, data.glucose, color='grey', size=1)

boxes = []
boxes.append(BoxAnnotation(top=80, fill_alpha=0.1, fill_color='red', line_color='red'))
boxes.append(BoxAnnotation(bottom=180, fill_alpha=0.1, fill_color='red', line_color='red'))
p.renderers.extend(boxes)
# p.add_layout(Legend(items=[LegendItem(label='Background', renderers=boxes)]))
show(p)
```

If I uncommet the line with the Legend I get the error message:
> ValueError: failed to validate LegendItem(id='2918', ...).renderers: expected an element of List(Instance(GlyphRenderer)), got seq with invalid items [BoxAnnotation(id='2916', ...), BoxAnnotation(id='2917', ...)]

### Feature description

This is possibly a dupliacte, I found this comment in #9955

> Allow annotations to be added to legends (this has come up several times recently)

### Additional information

I am using Bokeh 2.4.2 and don't know if this already is changed on the development branch.
user: mosc9575
issue_12107: state: open
title: [FEATURE] TickFormatter to support SI prefix units
body: ### Problem description

In my work I use SI prefix units a lot and I have not been able to get `NumeralTickFormatter` to style tick labels as I would prefer when using `format` argument. For one thing, it seems to mix prefix's which I do not think is always appropriate. In the figure shown below the y-axis is using `NumeralTickFormatter(format=" a")`. Upper tick labels are with 10^3 prefix `k` but tick labels < 1000 are regular numbers w/o prefix.

<img width="386" alt="Skærmbillede 2022-04-27 kl  15 51 50" src="https://user-images.githubusercontent.com/33606696/165534403-2e314bd4-b427-43bc-bcb7-e2631e1a2a9e.png">

By having a specific `TickFormatter` that supports SI prefix, I would assume this format easily can be added to `HoverTool` as a format a numbers.

### Feature description

I propose to have a `TickFormatter` that supports SI prefix with added support for a specific suffix one would like to have eg. "Hz". Some examples are shown in the figure below based on a custom extension of `TickFormatter` that I have tried to write (SIFormatter). From the figure one will observe
1. Same prefix for all tick labels
2. Prefix adhere to SI specification, hence million is capital M and not lower m
3. One can add a suffix eg. HZ in this case

<img width="1210" alt="Skærmbillede 2022-04-27 kl  15 59 15" src="https://user-images.githubusercontent.com/33606696/165537345-35cb5f5c-3924-4187-bb73-9cbdb91caf74.png">


### Potential alternatives

As mentioned above I ventured out into a custom extension. 

```
from bokeh.io import save, output_file
from bokeh.models import TickFormatter, NumeralTickFormatter
from bokeh.plotting import figure
from bokeh.layouts import gridplot
from bokeh.util.compiler import TypeScript
from bokeh.core.properties import Auto, Either, Int, String

output_file("si_tickformatter.html")

TS_CODE = """
// Bureau International des Poids et Mesures
// The International System of Units (SI)
// 9th edition 2019
// 
// Table 7. SI prefixes
// _____________________________________________________
// Factor   Name    Symbol      Factor  Name    Symbol
// 10^1     deca    da          10^-1   deci    d
// 10^2     hecto   h           10^-2   centi   c
// 10^3     kilo    k           10^-3   milli   m
// 10^6     mega    M           10^-6   micro   μ
// 10^9     giga    G           10^-9   nano    n
// 10^12    tera    T           10^-12  pico    p
// 10^15    peta    P           10^-15  femto   f
// 10^18    exa     E           10^-18  atto    a
// 10^21    zetta   Z           10^-21  zepto   z
// 10^24    yotta   Y           10^-24  yocto   y
// _____________________________________________________
// ref.: https://www.bipm.org/documents/20126/41483022/SI-Brochure-9-EN.pdf/2d2b50bf-f2b4-9661-f402-5f9d66e4b507?version=1.10&t=1632138671693&download=true
//
// Currently deca, hecto, deci, centi are not used.
// Implemented to be a suffix to the axis ticks.

import {TickFormatter} from "models/formatters/tick_formatter"
import * as p from "core/properties"

let factorMapping: any = {
    '1': 'da', '2': 'h', '3': 'k', '6': 'M', '9': 'G',
    '12': 'T', '15': 'P', '18': 'E', '21': 'Z', '24': 'Y',
    '-1': 'd', '-2': 'c', '-3': 'm', '-6': 'μ', '-9': 'n',
    '-12': 'p', '-15': 'f', '-18': 'a', '-21': 'z', '-24': 'y'
};

export namespace SIFormatter {
  export type Attrs = p.AttrsOf<Props>

  export type Props = TickFormatter.Props & {
    precision: p.Property<number | "auto">,
    suffix: p.Property<string>
  }
}

export interface SIFormatter extends SIFormatter.Attrs {}

export class SIFormatter extends TickFormatter {
  // TickFormatters should implement this method, which accepts a list
  // of numbers (ticks) and returns a list of strings where numbers 
  // converted to appropiate SI symbol
  properties: SIFormatter.Props

  constructor(attrs?: Partial<SIFormatter.Attrs>) {
    super(attrs)
  }

  static {
    this.define<SIFormatter.Props>(({Or, Int, Auto, String}) => ({
      precision: [ Or(Int, Auto), 'auto' ],
      suffix: [ String, '' ]
    }))
  }

  doFormat(ticks: number[], _opts: {loc: number}): string[] {
    var factorStr = '';
    const highThreshold = 10000;
    const lowThreshold = 0.1;
    const maxVal = Math.max(...ticks);
    const formatted = [];
    
    var base = Math.floor(Math.log10(Math.abs(maxVal)));
    var base = Math.floor(base / 3);
    var factor = base * 3;

    for (let i = 0, len = ticks.length; i < len; i++) {
        if (base === 0) {
            var newTickVal = ticks[i];
        } else if ((maxVal < highThreshold) && (maxVal > lowThreshold)) {
            var newTickVal = ticks[i];
        } else {
            var newTickVal = ((ticks[i]) / Math.pow(10, factor));

            // Sometimes I experience ticks with trailing numbers eg. 0.006000000001
            // This is an attempt to check for this and remove eg. 0.000000000001
            // Not sure if a JS function exist for this?
            // Or should it be handled in TickFormatter?

            const remain = newTickVal % (Math.trunc(newTickVal));
            if (remain < 1e-8) {
                newTickVal -= remain;
            }

            factorStr = ' ' + factorMapping[factor];
        }
        if (this.precision != 'auto') {
            var newTickStr = newTickVal.toFixed(this.precision);
        } else {
            var newTickStr = newTickVal.toString();
        }
        if ((factorStr == '') && (this.suffix != '')) {
            factorStr = ' ';
        } 
        formatted.push(newTickStr + factorStr + this.suffix);
    }
    return formatted;
  }
}
"""


class SIFormatter(TickFormatter):

    __implementation__ = TypeScript(TS_CODE)

    precision = Either(Auto, Int, help="""
    How many digits of precision to display in tick labels.
    """)

    suffix = String(default="", help="""
    Any suffix to add to ticks labels eg. Hz.
    """)


def add_figure(data):
    p = figure(plot_width = 400, plot_height = 300)
    p.title = data['title']
    p.circle(x = data['x'], y = data['y'], size = 10)
    p.yaxis.formatter = data['y_formatter']

    return p


x = [1, 2, 3, 4, 5, 6]
data_sets = {
    1: {
        'x': x,
        'y': [100, 200, 500, 1000, 1200, 1600],
        'y_formatter': NumeralTickFormatter(format="0 a"),
        'title': 'NumeralTickFormatter(format="0 a")'
    },
    2: {
        'x': x,
        'y': [100, 200, 500, 1000, 1200, 1600],
        'y_formatter': SIFormatter(),
        'title': 'SIFormatter()'
    },
    3: {
        'x': x,
        'y': [100, 2e3, 6e3, 4e4, 5e5, 6e6],
        'y_formatter': NumeralTickFormatter(format="0 a"),
        'title': 'NumeralTickFormatter(format="0 a")'
    },
    4: {
        'x': x,
        'y': [100, 2e3, 6e3, 4e4, 5e5, 6e6],
        'y_formatter': SIFormatter(),
        'title': 'SIFormatter()'
    },
    5: {
        'x': x,
        'y': [1e-4, 2e-4, 6e-5, 4e-5, 5e-6, 6e-6],
        'y_formatter': SIFormatter(),
        'title': 'SIFormatter()'
    },
    6: {
        'x': x,
        'y': [1e-4, 2e-4, 6e-5, 4e-5, 5e-6, 6e-6],
        'y_formatter': SIFormatter(suffix = 'Hz'),
        'title': 'SIFormatter(suffix = "Hz")'
    },
    7: {
        'x': x,
        'y': [1e-4, 2e-4, 6e-5, 4e-5, 5e-6, 6e-6],
        'y_formatter': SIFormatter(precision = 2, suffix = 'Hz'),
        'title': 'SIFormatter(precision = 2, suffix = "Hz")'
    }
} 

plots = []
for d, v in data_sets.items():
    plots.append(add_figure(v))    


save(gridplot(plots, ncols = 3), title = 'SI tickformatter')
```

### Additional information

My Bokeh setup is as follows
```
python3 -m bokeh info
Python version      :  3.9.12 (main, Mar 26 2022, 15:52:10) 
IPython version     :  (not installed)
Tornado version     :  6.1
Bokeh version       :  2.4.2
BokehJS static path :  /.venv/lib/python3.9/site-packages/bokeh/server/static
node.js version     :  v16.14.2
npm version         :  8.5.0
Operating system    :  macOS-11.6-x86_64-i386-64bit

```
user: carve11
issue_12095: state: open
title: ESRI IMAGERY tiles turn grey when zoomed in
body: Hi there,

It would be great to set the ESRI’s " NoDataTileBehavior", so you could zoom in further without the map turning grey.
https://developers.arcgis.com/qt/cpp/api-reference/maptypes-h.html#NoDataTileBehavior-enum

Example with Bokeh 2.4.2 (run this then zoom in all the way until it turns grey to see what it does):

```
from bokeh.plotting import figure, output_file, show
from bokeh.tile_providers import ESRI_IMAGERY, get_provider

tile_provider = get_provider(ESRI_IMAGERY)

# range bounds supplied in web mercator coordinates
p = figure(x_range=(-2000000, 6000000), y_range=(-1000000, 7000000),
           x_axis_type="mercator", y_axis_type="mercator")
p.add_tile(tile_provider)

show(p)
```



user: HuxleyJay
issue_12077: state: open
title: [BUG] Figure x_range and y_range DataRange1d stops working when a renderer view changes while the plot is not displayed
body: ### Software versions

Python version      :  3.7.4 (default, Aug 13 2019, 15:17:50) 
IPython version     :  7.31.1
Tornado version     :  5.1.1
Bokeh version       :  2.4.2
BokehJS static path :  /Users/redacted/miniconda2/envs/redacted/lib/python3.7/site-packages/bokeh/server/static
node.js version     :  (not installed)
npm version         :  (not installed)
Operating system    :  Darwin-19.6.0-x86_64-i386-64bit



### Expected behavior

When updating the view property of a renderer in a figure, even if the figure is not displayed on the dashboard, I would expect for the x and y ranges to still update dynamically when the figure is displayed again.

### Observed behavior

When updating the view property of a renderer in a figure when the figure is not displayed in the dashboard, the x and y ranges stop updating dynamically when the when the figure is displayed again.

To recreate the issue, I created a file called testapp.py and ran

` 
bokeh serve --show testapp.py
`

1. Press the 'change filter' button a few times. The filter on the plot changes and the plots dynamically update.
2. Press the 'replace plot 1' button. Plot 1 is replaced with a text input in the layout. Press the 'change filter' button a few times. Press the 'replace plot 1' button to replace the text input with plot 1 again. Now press the 'change filter' button a few times and see that plot 1 still autoscales (this is because of the workaround). This is the expected behavior.
3. Press the 'replace plot 2' button. Plot 2 is replaced with a text input in the layout. Press the 'change filter' button a few times. Press the 'replace plot 2' button to replace the text input with plot 2 again. Now press the 'change filter' button a few times and see that plot 2 does not auto scale anymore. This is unexpected behavior.


Thank you for looking into this!

### Example code

```Python
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource, TextInput, Button, CDSView, IndexFilter, DataRange1d
from bokeh.io import curdoc, show
from bokeh.layouts import column, row
import pandas as pd


class TestApp:
    def __init__(self):
        self.plot_1 = figure(width=400, height=300, title='plot 1 - workaround implemented')
        self.plot_2 = figure(width=400, height=300, title='plot 2 - no workaround implemented')
        self.source = ColumnDataSource(pd.DataFrame.from_dict({'x': [1, 2, 3], 'y': [1, 2, 3]}))
        self.filters = [IndexFilter([0, 1, 2])]
        self.view = CDSView(source=self.source, filters=self.filters)
        self.plot_1.scatter(source=self.source, x='x', y='y', view=self.view)
        self.plot_2.scatter(source=self.source, x='x', y='y', view=self.view)
        self.text_box = TextInput(title='title')
        self.button_1 = Button(label='replace plot 1')
        self.button_2 = Button(label='replace plot 2')
        self.button_3 = Button(label='change filter')
        self.column_1 = column(self.button_1, self.button_2, self.button_3)
        self.row_1 = row(children=[self.column_1, self.plot_1, self.plot_2])
        self.button_1.on_click(self.replace_plot_1)
        self.button_2.on_click(self.replace_plot_2)
        self.filter_flip = True
        self.button_3.on_click(self.change_filter)
        curdoc().add_root(self.row_1)
        show(self.row_1)

    def replace_plot_1(self):
        if self.row_1.children[1] == self.plot_1:
            self.row_1.children[1] = self.text_box
        else:
            self.row_1.children[1] = self.plot_1

    def replace_plot_2(self):
        if self.row_1.children[2] == self.plot_2:
            self.row_1.children[2] = self.text_box
        else:
            self.row_1.children[2] = self.plot_2

    def change_filter(self):
        if self.filter_flip:
            self.filters = [IndexFilter([0, 1])]
        else:
            self.filters = [IndexFilter([0, 1, 2])]
        self.filter_flip = not self.filter_flip
        self.view = CDSView(source=self.source, filters=self.filters, name='cds_view')
        self.plot_1.renderers[0].view = self.view
        self.plot_2.renderers[0].view = self.view
        # Only replacing the x and y range on plot_1 to demonstrate the workaround
        self.plot_1.x_range = DataRange1d()
        self.plot_1.y_range = DataRange1d()


TestApp()
```
user: monadandan
issue_12073: state: open
title: Add user guide section about sub-coordinates/subplots
body: Based on the discussion in https://github.com/bokeh/bokeh/pull/12068, I noticed we don't have a user guide section explaining the functionality used in that example. We should add information about `subplot()` to the user guide!

user: tcmetzger
issue_12069: state: open
title: [BUG] Long x axis labels misaligned on charts
body: ### Software versions

Python version      :  3.8.12 | packaged by conda-forge | (default, Oct 12 2021, 21:22:46) [MSC v.1916 64 bit (AMD64)]
IPython version     :  8.2.0
Tornado version     :  6.1
Bokeh version       :  2.4.2
BokehJS static path :  C:\ProgramData\CooperConda\lib\site-packages\bokeh\server\static
node.js version     :  (not installed)
npm version         :  (not installed)
Operating system    :  Windows-10-10.0.19042-SP0

### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

We recently upgraded from Bokeh 1.3.4 to 2.4.2. In 1.3.4, when using rotated x axis labels on a bar chart, long labels would extend off of the chart space as in the below example:

![image](https://user-images.githubusercontent.com/6414946/161128347-8b770b4a-37ad-4710-bd57-1991f11d7aa8.png)

### Observed behavior

When the same chart is created with 2.4.2, the leftmost label is shifted away from its bar and renders over other text:

![image](https://user-images.githubusercontent.com/6414946/161128568-72d96fcf-82d2-450e-af73-d15bf80ea243.png)

The ideal behavior might be to allow an option that causes the chart to shrink to accomodate the long text so that it isn't "chopped" off, but the behavior in 1.3.4 is probably better than 2.4.2.

### Example code

```Python
from bokeh.io import output_file, show
from bokeh.plotting import figure

fruits = [
    'Apples Apples Apples Apples Apples Apples Apples Apples Apples', 
    'Pears Pears Pears Pears Pears Pears Pears Pears Pears Pears Pears', 
    'Nectarines Nectarines Nectarines Nectarines Nectarines Nectarines', 
    'Plums Plums Plums Plums Plums Plums Plums Plums Plums Plums Plums Plums', 
    'Grapes Grapes Grapes Grapes Grapes Grapes Grapes Grapes Grapes Grapes', 
    'Strawberries Strawberries Strawberries Strawberries Strawberries Strawberries'
]
counts = [5, 3, 4, 2, 4, 6]

# sorting the bars means sorting the range factors
sorted_fruits = sorted(fruits, key=lambda x: counts[fruits.index(x)])

p = figure(x_range=sorted_fruits, height=500, title="Fruit counts",
           toolbar_location=None, tools="")

p.vbar(x=fruits, top=counts, width=0.9)

p.xgrid.grid_line_color = None
p.y_range.start = 0
p.xaxis.major_label_orientation = 1.0472
show(p)
```


### Stack traceback or browser console output

_No response_

### Screenshots

_No response_
user: mart00n
issue_12048: state: open
title: [FEATURE] Taptool / Tap Event: capture "hit" glyphs (and properties) as a selection
body: ### Problem description

I'd like to be able to query properties of glyphs that are "hit" by a `TapTool` / `Tap` event.

See [discussion here](https://discourse.bokeh.org/t/get-glyph-name-using-taptool/8978/6)

### Feature description

Renderers / glyphs "hit" by a tap are captured as a list-like selection.

E.g.: `TapTool.selected` (tool) and `Tap.selected` (event) return a list of renderers hit by the tap.

So  `TapTool.selected[0].name` would return the name of the first renderer tapped,  `TapTool.selected[0].line_color` returns its color, etc.

### Potential alternatives

A possible workaround is to create individual tools for each renderer (and possibly restrict the names property of each tool to only the associated renderer's name), [as described by gmerritt123 here](https://discourse.bokeh.org/t/get-glyph-name-using-taptool/8978/8).

### Additional information

_No response_
user: RollOnRohlOff
issue_12043: state: open
title: Add support for binary serialization
body: Early WIP. This adds fully binary protocol on top of existing binary serialization of buffers (ndarrays, etc.). For now this PR supports one way serialization (Python -> JS). The other three ways need to be still implemented. This implementation is based on msgpack, though potentially better bases were suggested, but require further investigation. This PR amends existing protocol, so can be merged at later time without breaking changes.
user: mattpap
issue_12041: state: open
title: [BUG] CDSView/CustomJSFilter correct rendering failing dependent on number of indices?
body: ### Software versions

Python version      :  3.7.10 | packaged by conda-forge | (default, Sep 13 2021, 19:12:52) [MSC v.1916 64 bit (AMD64)]
IPython version     :  7.29.0
Tornado version     :  6.1
Bokeh version       :  2.4.2
BokehJS static path :  C:\ProgramData\Anaconda3\envs\geo_env37v2\lib\site-packages\bokeh\server\static
node.js version     :  v16.12.0

### Browser name and version

Chrome: Version 96.0.4664.45 (Official Build) (64-bit

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

This may be related to https://github.com/bokeh/bokeh/issues/11969 , but after a crap tonne of troubleshooting/trying to isolate, I may have a more helpful MRE.

I'm creating two figures:

f1 displays a scatter plot of x-y data. The user makes an arbitrary selection of these points, and that selection is used to filter a MultiLine glyph plotted on the second figure (f2). Both renderers are being driven by the same CDS (i.e. it contains 'x' and 'y' fields for the Scatter and 'xs' and 'ys' fields for the MultiLine.

### Observed behavior

From what I can tell, depending on how many records I make the initial dataframe (i.e. the length of my CDS), some of the higher indices won't render when I filter for only them.

What's super bizarre to me is that since I'm console.logging src.selected.indices, you can actually see the filter returning the correct indices in the console... but they aren't showing up on the plot, but this seems to only occur for indices above 65?!?

What's probably even more super bizarre, for less than 100 records, the magic number seems to be 65 --> if I select anything above 65, they won't show up on f2. This changes when the number of records goes beyond 100 though :-/ , like for 250 records it seems to happen for indices beyond 225?! And then, inexplicably, for some other lengths (like try 100 for instance), this issue doesn't show up at all!

I'll drop some gifs below trying to demonstrate. 

### Example code

```Python
# -*- coding: utf-8 -*-
"""
Created on Tue Mar  8 15:20:19 2022

@author: Gaelen
"""
import numpy as np
import pandas as pd
from bokeh.plotting import figure, show
from bokeh.models import MultiLine, CustomJSFilter, CDSView, ColumnDataSource,Scatter,CustomJS
from bokeh.layouts import layout

#try this with different lengths (e.g. try 65,92,100,250)
df = pd.DataFrame(data={'x':range(92)})
df['y'] = 0

#create horizontal lines
df['xs'] = list(zip(df['x'],df['x']+1))
df['ys'] = list(zip(df['x'],df['x']))
print(df.dtypes)
#I tried building the CDS a number of ways, got same behaviour

# src = ColumnDataSource(df) #straight pass in of dataframe
# src = ColumnDataSource({c:df[c].tolist() for c in df.columns}) #pass in of lists
src = ColumnDataSource({c:np.array(df[c]) for c in df.columns}) #pass in of np.arrays

#first figure, user to make a selection
f1 = figure(tools=['wheel_zoom','lasso_select'],width=1200)
f1_glyph = Scatter(x='x',y='y')
f1_rend = f1.add_glyph(src,f1_glyph)
#second figure, user to view the corresponding horizontal lines in that selection
f2 = figure(width=1200)
f2_glyph = MultiLine(xs='xs',ys='ys')
filt = CustomJSFilter(args=dict(src=src)
                      ,code='''
                      //inspect the selected indices
                      //note that if the datasource is
                      console.log(src.selected.indices)                      
                      return src.selected.indices''')
view = CDSView(source=src,filters=[filt])
src.selected.js_on_change('indices',CustomJS(args=dict(view=view)
                                             ,code='''view.properties.filters.change.emit()'''))
f2_rend = f2.add_glyph(src,f2_glyph
                        ,view=view
                       )

lo = layout([f1,f2])
show(lo)
```


### Stack traceback or browser console output

_No response_

### Screenshots

Example with 92 points/lines:

![bug92](https://user-images.githubusercontent.com/58000181/157333703-680d9f06-a601-4ac2-937a-4d1d2e4080b7.gif)


Example with 225:
![bug250](https://user-images.githubusercontent.com/58000181/157334320-69d2ff6b-6485-47f1-b659-ece3529d5299.gif)


user: gmerritt123
issue_12038: state: open
title: Apply dataclasses where appropriate
body: As noted in https://github.com/bokeh/bokeh/pull/12036 there are classes such as `RenderRoot`, etc. that could be simplified by re-writing them as Python dataclasses. Python dataclasses should be employed any place they can help reduce the codebase. 

user: bryevdv
labels: tag: codebase: Improvements to code quality without affecting any functionality.
issue_12037: state: open
title: Automatically version server app source code links
body: ### Expected behavior

The page at https://docs.bokeh.org/en/latest/docs/gallery.html links to the examples in the branch that corresponds to the selected Bokeh version in the version selector in the top left corner.

### Observed behavior

The examples always link to `master`.

user: p-himik
issue_12023: state: open
title: [BUG] Bokeh doesn't wait until all scripts loaded before embedding document in jupyter notebook
body: ### Software versions

Python version : 3.9.7 (default, Sep 16 2021, 08:50:36)
IPython version : 7.31.1
Tornado version : 6.1
Bokeh version : 2.4.2
BokehJS static path : /Users/stas/.pyenv/versions/anaconda3-2021.11/lib/python3.9/site-packages/bokeh/server/static
node.js version : v17.4.0
npm version : 8.3.1
Operating system : macOS-10.15.7-x86_64-i386-64bit

### Browser name and version

Chrome Version 98.0.4758.102 (Official Build) (x86_64) 

### Jupyter notebook / Jupyter Lab version

jupyterlab : 3.2.9

### Expected behavior

Bokeh waits until all scripts loaded and displays widgets correctly.

### Observed behavior

I'm using bokeh widgets and convert jupyter notebook to html using command:
```bash
jupyter nbconvert bug.ipynb --to html
```
What I observed is that if I disable script caching and try to load resulting html page I get the following error `Model 'Slider' does not exist. This could be due to a widget or a custom model not being registered before first usage.`.

 It looks like Bokeh calls embed_document before all scripts (including widgets) are loaded and that is why this error occurs.

https://github.com/bokeh/bokeh/blob/abde4f48aa2df7c85255414c367a8baf8bb149d0/bokeh/core/_templates/try_run.js#L11-L14

Maybe an additional condition should be added here?

```javascript
 if (root.Bokeh !== undefined && root._bokeh_is_loading === 0) { 
```

Though with caching enabled or if I use `output_notebook(resources=INLINE)` it works fine.

### Example code

```Python
from bokeh.plotting import output_notebook, show
from bokeh.models import Slider

output_notebook()

show(Slider(value=0, start=0, end=10))
```


### Stack traceback or browser console output

```
Uncaught Error: Model 'Slider' does not exist. This could be due to a widget or a custom model not being registered before first usage.
    at _.get (bokeh-2.4.2.min.js:166:1015)
    at Function._instantiate_object (bokeh-2.4.2.min.js:165:4956)
    at Function._instantiate_references_json (bokeh-2.4.2.min.js:165:5144)
    at Function.from_json (bokeh-2.4.2.min.js:165:8794)
    at Object.t.embed_items_notebook (bokeh-2.4.2.min.js:591:2451)
    at embed_document (test_bokeh_load.html:14931:20)
    at test_bokeh_load.html:14941:9
```

### Screenshots

https://user-images.githubusercontent.com/4602302/155892308-f8e1cbdd-f039-4bbd-9e4f-65cefc937522.mp4


user: stas-sl
issue_11998: state: open
title: [BUG] Hover is slow when hover_color is set
body: ### Software versions

Python version      :  3.9.7 (default, Sep 16 2021, 08:50:36)
IPython version     :  8.0.1
Tornado version     :  6.1
Bokeh version       :  2.4.2
BokehJS static path :  /Users/stas/.pyenv/versions/anaconda3-2021.11/lib/python3.9/site-packages/bokeh/server/static
node.js version     :  v17.4.0
npm version         :  8.3.1
Operating system    :  macOS-10.15.7-x86_64-i386-64bit

### Browser name and version

Chrome 97.0.4692.99 (Official Build) (x86_64)

### Jupyter notebook / Jupyter Lab version

jupyterlab: 3.2.8

### Expected behavior

Hover works fast for 30K data points with or without hover_color

### Observed behavior

There is a noticeable slowdown when moving mouse over scatter plot with 30K data points if hover_color is set, though without hover_color it works really fast. I already mentioned it in another issue https://github.com/bokeh/bokeh/issues/11629, which is already closed. I'm not sure if this the same or related issue, but as it is still occurs, I thought it would be better to have an open issue, so it wouldn't lost.

### Example code

```Python
import bokeh
import numpy as np
import pandas as pd

from bokeh.plotting import figure, show
from bokeh.io import output_notebook

output_notebook()

n = 30000
ds = pd.DataFrame({'x': np.random.rand(n), 'y': np.random.rand(n)})

p = figure(tooltips=[('x', '@x'), ('y', '@y')])
p.scatter('x', 'y', source=ds, hover_color='red')
show(p)
```


### Stack traceback or browser console output

_No response_

### Screenshots

https://user-images.githubusercontent.com/4602302/153514967-d3a065f6-203c-4c0b-b0eb-8d489b2f38ac.mp4


user: stas-sl
issue_11991: state: open
title: Authentication doesn't work with prefix? [BUG] 
body: ### Software versions

Python version      :  3.8.12 | packaged by conda-forge | (default, Sep 29 2021, 19:14:40) [MSC v.1916 64 bit (AMD64)]
IPython version     :  7.28.0
Tornado version     :  6.1
Bokeh version       :  2.4.0
BokehJS static path :  C:\Users\...\Miniconda3\envs\...\lib\site-packages\bokeh\server\static
node.js version     :  v14.14.0
npm version         :  7.11.2


### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

I expected that login works when I supply a prefix (e.g. when using `bokeh serve` or initializing `Server` with a prefix).

### Observed behavior

It seems that the `login_url` variable isn't passed _with_ the prefix to Tornado when the initial redirect to the login endpoint occurs. 

This is what happens (when I have started the Bokeh app with a prefix set. Without the prefix, it works fine):

- I try to go to the index page of the app (http://localhost:5006/pre/app). 
- I am redirected to http://localhost:5006/login?next=%2Fpre%2Fapp, but it should be http://localhost:5006/pre/login?next=%2Fpre%2Fapp.
- If I go to the correct url, it works, but I get another 404 when I login and get redirected from there.
- If I (just to test) change the `login_url` to include the prefix, the initial redirect "works", but I don't reach the Bokeh endpoints since they will be on the form http://localhost:5006/pre/pre/login/.


In my actual code, I have created a work around by editing `login_url` after initializing the Server object:

```python
auth_provider = AuthModule('/path/to/module.py')
app = build_single_handler_application(full_path)
app = Server(
    {'/': app},
    prefix=prefix,
    auth_provider=auth_provider)
auth_provider._module.login_url = f'/{prefix}/login/'
```

### Example code

Run `bokeh serve app.py --auth-module=auth.py --prefix=pre`

```Python
# app.py

from bokeh.io import curdoc
from bokeh.models import Div


def modify_doc(doc):
    doc.title = 'Hello World'
    header = Div(text=f'<h1>{doc.title}</h1>')
    doc.add_root(header)


modify_doc(curdoc())

```



```python
# auth.py

from tornado.web import RequestHandler


login_url = '/login'


class LoginHandler(RequestHandler):
    def get(self):
        self.write('<html><body><form action="/login" method="post">'
                   'Name: <input type="text" name="name">'
                   '<input type="submit" value="Sign in">'
                   '</form></body></html>')

    def post(self):
        name = self.get_argument('name')
        self.set_cookie('user', name)
        self.redirect('/')


def get_user(request_handler):
    return request_handler.get_cookie('user')

```


### Stack traceback or browser console output

_No response_

### Screenshots

_No response_
user: alexanfl
issue_11983: state: open
title: [BUG] p.y_scale = LogScale() not being updated by push_notebook
body: ### Software versions

Python version      :  3.9.7 (default, Sep 16 2021, 08:50:36) 
IPython version     :  7.29.0
Tornado version     :  6.1
Bokeh version       :  2.4.2
BokehJS static path :  /Users/gsteele/anaconda3/envs/jan2022/lib/python3.9/site-packages/bokeh/server/static
node.js version     :  (not installed)
npm version         :  (not installed)
Operating system    :  macOS-10.16-x86_64-i386-64bit

### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

Selected Jupyter core packages... IPython          : 7.29.0 ipykernel        : 6.4.1 ipywidgets       : 7.6.5 jupyter_client   : 7.1.0 jupyter_core     : 4.9.1 jupyter_server   : not installed jupyterlab       : not installed nbclient         : 0.5.3 nbconvert        : 6.3.0 nbformat         : 5.1.3 notebook         : 6.4.6 qtconsole        : 5.1.1 traitlets        : 5.1.1

### Expected behavior

I would expect that if I set `p.y_scale = Logscale()` and then `push_notebook()` that the plot y scale should be come logscale.

### Observed behavior

It has no effect

### Example code

```Python
import numpy as np

from bokeh.models import ColumnDataSource
from bokeh.plotting import figure, show
from bokeh.io import output_notebook, push_notebook
from bokeh.models import LogScale, LinearScale


# This will import the latest compatible bokeh JS from the CDN
output_notebook()

# Generate data
x = np.linspace(0,100,1000)
y = 1/((x-50)**2 + 1) 
y += np.random.normal(size=len(x))*0.01
y += 1/((x-80)**2 + 5)

# Plot it
p = figure(height=200, width=600) 
p.sizing_mode = "scale_width"
p.line(x,y)
# If uncommented, this does work
# p.y_scale = LogScale()
target = show(p, notebook_handle=True)

# But this does nothing
p.y_scale = LogScale()
push_notebook(handle=target)
```


### Stack traceback or browser console output

_No response_

### Screenshots

_No response_
user: gsteele13
issue_11967: state: open
title: Update docs for FileInput
body: Following the discussion in https://discourse.bokeh.org/t/file-name-disappears-after-clicking-the-cancel-button/8803, the documentation for `FileInput` could use some clarification:

* reference guide: https://docs.bokeh.org/en/latest/docs/reference/models/widgets/inputs.html?highlight=fileinput#fileinput 
* user guide: https://docs.bokeh.org/en/latest/docs/user_guide/interaction/widgets.html?highlight=fileinput#fileinput
user: tcmetzger
issue_11936: state: open
title: [BUG] Model property changed from int to bool
body: ### Software versions

Python version      :  3.8.6 (tags/v3.8.6:db45529, Sep 23 2020, 15:52:53) [MSC v.1927 64 bit (AMD64)]
IPython version     :  7.24.1
Tornado version     :  6.1
Bokeh version       :  2.4.2
BokehJS static path :  C:\Users\xa235208\Documents\Python\pyenv\py38\lib\site-packages\bokeh\server\static 
node.js version     :  v14.15.1
npm version         :  7.10.0
Operating system    :  Windows-10-10.0.19041-SP0

### Expected behavior

When creating a new model with a property which can take a boolean or an int I expect the property type conserved


### Observed behavior

However if I create a Model wih the following property : Either(Int, Bool, default=True)
And if I pass the value 1 to the model the property is a boolean True

### Example code

```Python
from bokeh.models import Model
from bokeh.core.properties import Either, Int, Bool, String
class Test(Model):
    bool_or_int = Either(Int, Bool, default=True)
t1 = Test(bool_or_int=1)
t2 = Test(bool_or_int=2)
print(t1.bool_or_int, t2.bool_or_int)
```


### Stack traceback or browser console output

_No response_

### Screenshots

_No response_
user: xavArtley
issue_11916: state: open
title: [BUG] Setting a plot's active tool triggers a time-consuming call to recompute_all_models()
body: ### Software versions

Python version      :  3.9.6 | packaged by conda-forge | (default, Jul 11 2021, 03:36:15) 
IPython version     :  7.26.0
Tornado version     :  6.1
Bokeh version       :  3.0.0dev1+95.ga129565f0
BokehJS static path :  /Users/edbraun/opt/anaconda3/envs/bkdev/lib/python3.9/site-packages/bokeh-3.0.0.dev1+95.ga129565f0-py3.9.egg/bokeh/server/static
node.js version     :  v16.6.1
npm version         :  7.24.2
Operating system    :  macOS-11.6.1-x86_64-i386-64bit

### Browser name and version

Google Chrome 96.0.4664.110 (Official Build) (x86_64)

### Expected behavior

I would expect that setting a plot's active tool programmatically would be almost as fast as having the user changing the active tool in the browser.

### Observed behavior

Apparently setting a figure's active tool programmatically triggers a call to `recompute_all_models()`. This isn't such a big deal when there aren't many Bokeh models in the document, but when the number of models is large, this can take quite a long time. In the example I give below with 400 Bokeh models, it takes over a second for the server to respond and over 0.1 seconds on the browser.

I don't think this call should be necessary if the models aren't actually being changed, no?

### Example code

```Python
from bokeh.io import curdoc
from bokeh.layouts import column
from bokeh.models import Select, Button
from bokeh.plotting import figure
import time

# Set up widgets
figures = {}
figures[0] = figure(tools="pan,wheel_zoom,box_zoom,reset")
for i in range(1, 400):
    figures[i] = figure(tools="pan,wheel_zoom,box_zoom,reset", visible=False)
button1 = Button(label='Change active drag tool')

# Set up callback
def update_select_direct(attrname):
    start = time.time()
    figures[0].toolbar.active_drag = figures[0].toolbar.tools[2]
    print(time.time()-start)

button1.on_click(update_select_direct)

# Set up layouts and add to document
curdoc().add_root(column(*[figure for figure in figures.values()], button1))
```


### Screenshots

This shows the call to `_recompute_all_models` in the browser, which takes 151 ms. This, combined with the ~1100 ms response from the server side, makes the program very laggy.

<img width="1904" alt="Screen Shot 2021-12-29 at 6 14 22 PM" src="https://user-images.githubusercontent.com/6618019/147709949-b6ef0e79-b175-42fd-91fa-708db9c5a2df.png">

user: EfremBraun
issue_11897: state: open
title: Process example metadata for all examples
body: Currently example metadata is only processed and rendered for the subset of examples that are included in the gallery. One long term goal with the metadata was to have a standalone, indexable page for *every* example, even ones not prominently on display in the gallery. With more new contributors pitching in for https://github.com/bokeh/bokeh/issues/11765 it is becoming more pressing to get that working so that the metadata additions are actually verified correct in CI, and so that the users can see tangible results of their contribution. (I expect some of the existing contributions may have small issues that will need fixing up)

cc @tcmetzger 
user: bryevdv
issue_11895: state: open
title: Update extension examples
body: Now that Bokeh includes support for rendering LaTeX, the old extension examples using Katex were misleading and removed in PR #11894. This means that we now need new examples to demonstrate how to create a wrapped extension (following the general idea of the removed examples but avoiding LaTeX or Katex). 
Once we have those new examples, the unit test in `tests/unit/bokeh/embed/latex_label/` also need to be updated accordingly.
user: tcmetzger
issue_11860: state: open
title: [FEATURE] Improve mobile usability on Bokeh sliders
body: ### Problem description

I am fine tuning a Bokeh web application that we managed to make somewhat responsive and render rather acceptably on mobile devices. The weak spot the mobile rendering is related to sliders:

* we can't adjust the font size (it is too small)
* we can't adjust the slider actionable area

Due to this, we need temporary zooming and very high thumb precision.

### Feature description

We would to be able to adjust the slider title font size and to adjust the slider actionable area. A mockup of the intended effect is attached.

![mobile-comparison](https://user-images.githubusercontent.com/3352900/144933525-0caa0eeb-3d12-400d-a58d-f8239b42c9f3.png)


### Potential alternatives

In an ideal world everything would auto-resize to the appropriate dimensions, but there are so many combinations of devices, resolutions and orientations that it seems impossible to get right. Having control over the slider appearance seems like the best approach.

### Additional information

_No response_
user: ghomem
issue_11854: state: open
title: [BUG] Unsupported title strings in TableColumn 
body: ### Software versions

Python version      :  3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)]
IPython version     :  (not installed)
Tornado version     :  6.1
Bokeh version       :  2.4.2


### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

TableColumn titles should support and render any ASCII string.

### Observed behavior

TableColumn titles are not completed rendered in a table if '<' characters are involved that are followed by most ASCII characters.

### Example code

```Python
from bokeh.layouts import column
from bokeh.models import ColumnDataSource, DataTable, TableColumn
from bokeh.plotting import curdoc

source = ColumnDataSource()
table1 = DataTable(source=source, columns=[TableColumn(field="ab", title="a<b")]) #column title is rendered as "a" NOT the intented "a<b"!
table2 = DataTable(source=source, columns=[TableColumn(field="ab", title="a< b")]) #column title is rendered as "a< b" as expected
table3 = DataTable(source=source, columns=[TableColumn(field="ab", title="a<=b")]) #column title is rendered as "a<=b" as expected

curdoc().add_root(column(table1, table2, table3))
```


### Stack traceback or browser console output

_No response_

### Screenshots

_No response_
user: muendlein
issue_11851: state: open
title: Mixing linear/log axis
body: ### Software versions

Python version      :  3.8.8 (default, Apr 13 2021, 15:08:03) [MSC v.1916 64 bit (AMD64)]
IPython version     :  7.22.0
Tornado version     :  6.1
Bokeh version       :  2.3.2
BokehJS static path :  C:\ProgramData\Anaconda3\lib\site-packages\bokeh\server\static
node.js version     :  (not installed)
npm version         :  (not installed)

### Browser name and version

_No response_

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

Currently as far as I know, LinearAxis and LogAxis can't be mixed on the same plane.

Reproducible example below where I've got a single datasource for two multiline glyphs. Both have the same xs values. One glyph's ys follows a log scale best while the other follows linear. I want to see a linear scale on the primary (left) y axis and a log scale on the right one, with one glyph following the linear axis and the other following the log axis.

### Observed behavior

If LogAxis layout gets added as a layout, the log-spec'd renderer doesn't plot logarithmically axis and the log axis doesn't display. If you comment that out and add it as a LinearAxis instead, basically the same thing renders except now the axis displays.

### Example code

```Python
from bokeh.models import DataRange1d, Range1d, MultiLine, CustomJS,LinearAxis,LogAxis
from bokeh.models import ColumnDataSource, HoverTool
from bokeh.plotting import figure, save, show

src = ColumnDataSource(data={'xs':[[1,2,3,4,5,6],[1,2,3,4,5,6]]
                             ,'ys':[[1,2,5,6,7,8],[2,6,8,9,9.1,10,11]]
                             ,'zs':[[1,10,100,1000,10000,100000],[1,20,80,800,9000,110000]]})

f = figure()
f.extra_y_ranges={'z':DataRange1d()}

#try to add a log secondary y axis
f.add_layout(LogAxis(y_range_name='z'),'right')
# f.add_layout(LinearAxis(y_range_name='z'),'right')

g1 = MultiLine(xs='xs',ys='ys',line_color='blue')
r1 = f.add_glyph(src,g1)
f.y_range.renderers=[r1]

g2 = MultiLine(xs='xs',ys='zs',line_color='red')
r2 = f.add_glyph(src,g2,y_range_name='z')
f.extra_y_ranges['z'].renderers=[r2]

show(f)
```
```


### Stack traceback or browser console output

_No response_

### Screenshots

_No response_
user: gmerritt123
issue_11848: state: open
title: [FEATURE] Add ability to provide custom CDN URL for bokeh extensions
body: ### Problem description

Bokeh extensions can either be served by the Bokeh server or fetch the extension CSS/JS from a CDN. Currently the CDN the extension is loaded from is hardcoded here:

https://github.com/bokeh/bokeh/blob/2cdfb28c30a91918253965eee6758a06224a2cb2/bokeh/embed/bundle.py#L319

Since unpkg is a free service it has no guarantees on speed or reliability (although it generally does pretty well at both). Therefore it would be nice if we could also provide an alternative CDN host, whether that is another hosted service or your own service.

### Feature description

It would be nice if an extension could provide a custom CDN URL in it's `package.json` file which will override the `_default_cdn_url`.

### Potential alternatives

Alternatively we might want to offer even more control over the extension resources URLs and allow extensions to define a custom function which is responsible for generating the appropriate URLs.

### Additional information

_No response_
user: philippjfr
issue_11817: state: open
title: [BUG] Updating x and y ranges of figure doesn't work
body: ### Software versions

Python version      :  3.8.10 (default, Sep 28 2021, 16:10:42) 
IPython version     :  7.13.0
Tornado version     :  5.1.1
Bokeh version       :  2.4.1
BokehJS static path :  /home/kaymes/.local/lib/python3.8/site-packages/bokeh/server/static
node.js version     :  v16.13.0
npm version         :  8.1.0


### Browser name and version

Firefox 94.0 and Chromium 95.0.4638.69

### Jupyter notebook / Jupyter Lab version

_No response_

### Expected behavior

Changing the range of a plot by replacing the range objects should update the visible range of the figure. 
E.g.
`figure.update(x_range = bokeh.models.Range1d(100, 200), y_range = bokeh.models.Range1d(100, 200))`
should lead to the figure showing the range between 100 and 200 on both axes.

### Observed behavior

The figure didn't change it's visible range at all. Furthermore, any updates to the rages themselves such as `figure.x_range.update(start=100, end=200)` stop to have any effect on the visible area after the above call.

In the example code, clicking button3 works as expected at first.
Clicking any of button1 or button2 don't work.
After clicking any of button1 or button2, button3 stops working as well.

Note: simply always using the method of button3 is not an option because the x and y ranges get updated sequentially. This leads to race conditions because if the figure tries to maintain it's aspect, changing one also results in changes to the other. This is then attempted to be synced back to Python. All the while the second update is being pushed to the browser. As a result, it frequently happens that in the end the figure doesn't show the desired range at all.
Thus, I would like to use the button1 method of setting x and y ranges together in a single update so changing the visible range works reliably.

### Example code

```Python
import bokeh, bokeh.plotting, bokeh.models

figure = bokeh.plotting.figure(x_range=(0, 1000), y_range=(0, 1000))


def on_click1():
    figure.update(x_range = bokeh.models.Range1d(100, 200), y_range = bokeh.models.Range1d(100, 200))

button1 = bokeh.models.Button(label="Update ranges together")
button1.on_click(on_click1)


def on_click2():
    figure.x_range = bokeh.models.Range1d(200, 300)
    figure.y_range = bokeh.models.Range1d(200, 300)

button2 = bokeh.models.Button(label="Set ranges")
button2.on_click(on_click2)


def on_click3():
    figure.x_range.update(start=300, end=400)
    figure.y_range.update(start=300, end=400)

button3 = bokeh.models.Button(label="Change ranges")
button3.on_click(on_click3)


column = bokeh.plotting.column(children=[figure, button1, button2, button3])
bokeh.plotting.curdoc().add_root(column)
```


### Stack traceback or browser console output

_No response_

### Screenshots

_No response_
user: kaymes
issue_11798: state: open
title: Don't expose `nan` in ranges' API unnecessarily
body: This is a follow-up on changes from PR #11777, where ranges' API was changed to allow support for unset properties. This, however, exposed `nan` default values, which are cumbersome to work with. Additionally, this design is more an effect of other issues (e.g. properties vs. computed properties; especially in the particular case of ranges), than intentional API design on its own. This needs to be refined during 3.0 development.
user: mattpap
issue_11788: state: open
title: [BUG]: FileInput waits blocks Python callbacks until upload is finished
body: From
https://github.com/holoviz/panel/issues/2863

When uploading a big file, the watched events run very late (likely after the big file is uploaded). Is there a way to trigger loading before the upload so that the user knows something is running?
```
import panel as pn
pn.extension()
file_input = pn.widgets.FileInput()
button = pn.widgets.Button()

def loading(event):
    button.disabled = True

file_input.param.watch(loading, "filename")
pn.Column(file_input, button).servable()
```

A workaround is to use jscallbacks.
```
import panel as pn
pn.extension()

def reset(event):
    file_input.disabled = False
    progress.active = False

file_input = pn.widgets.FileInput()
progress = pn.widgets.Progress(active=False)
file_input.jscallback(
    args={"progress": progress},
    value="""
        progress.active = true;
        source.disabled = true;
    """
)
file_input.param.watch(reset, "value")
col = pn.Column(progress, file_input)
col.servable()
```
This kind of works; not immediate, but much faster to react: the jscallback tries to activate the progress bar and disable another upload, while the python watch waits until it finishes uploading to re-enable both widgets (using the bug as a feature to know when it finishes uploading :P)
user: andhuang-CLGX
issue_11779: state: open
title: Better example for Single Module Bokeh server app
body: The example here:

http://docs.bokeh.org/en/latest/docs/user_guide/server.html#single-module-format

is too complicated in aspects that are not important (irrelevant styling) and not clear enough in ways that matter (conveying the idea of linking Python/JS objects). I would suggest replacing it with a simpler example that does something more familiar, e.g. a selection on a dropdown changes data sets shown, or similar. And then also accompanying the code with screen video capture of the app in operation to compare to. 

cc @tcmetzger  for thoughts 
user: bryevdv
issue_11765: state: open
title: Add metadata to standalone examples
body: As a follow-up to #11540, we would like to add metadata to as many examples as possible. This will not only make the examples more usable as they are right now, but it will also open up new possibilities to search for and crosslink examples.

For the purpose of this issue, 'standalone examples' are all .py files in these folders in this repository:

* `examples/plotting`
* `examples/models`

## Prerequisites 

Adding metadata uses reStructuredText (ReST), like the rest of Bokeh's documentation. However, most of the work can probably be done without knowing much about ReST. To test the metadata, you need to build the docs locally. In order to do that, you first need to [set up a local development environment](https://docs.bokeh.org/en/latest/docs/dev_guide/setup.html), and then follow the instructions for [setting up and building Bokeh’s documentation](https://docs.bokeh.org/en/latest/docs/dev_guide/documentation.html#setting-up-and-building-bokeh-s-documentation).

## Adding metadata

The **metadata to add to the examples** are added as a multi-line strings comment at the beginning of each .py file, containing the following information:
* An intro sentence or two describing the example narratively (please add line breaks to keep line length to a max of 80 chars)
* Entries for a custom-made Sphinx directive called `bokeh-example-metadata` with the following data
    * `:sampledata:` the name of the sample data set, if applicable. This is the list of the available sets of sample data: https://docs.bokeh.org/en/latest/docs/reference/sampledata.html?highlight=sampledata#module-bokeh.sampledata. For example: `:sampledata: glucose` 
    * `:apis:` The paths to the API objects highlighted in this example. Can be one or several. For example `:apis: bokeh.layouts.column, bokeh.layouts.gridplot`. Please use the specific model, i.e. `bokeh.models.Circle` instead of `bokeh.models`.
    *  `:refs:` Sphinx references to the user guide that cover the functionalities highlighted in this example. This part uses breadcrumbs, i.e. `chapter > sub-section`. To find the name of the references, you need to check the ReST source code of each of the respective user guide chapters in the `sphinx/source/docs/user_guide/` directory of the repository. A reference is the name (with underscores) between `.. _` and `:` above a headline. For example: ``:refs: :ref:`userguide_plotting` > :ref:`userguide_plotting_scatter_markers`, :ref:`userguide_data` > :ref:`userguide_data_transforming``
    * `:keywords:` A list of comma-separated keywords to help finding this example. For example: :keywords: alpha, colormap, markermap, scatter

## Example

See https://docs.bokeh.org/en/latest/docs/gallery/marker_map.html (and its source https://github.com/bokeh/bokeh/blob/branch-3.0/examples/plotting/file/marker_map.py) for a complete example. This is the ReST source code at the beginning of the Python file:

```
''' A scatter plot using the `Palmer penguin dataset`_. This example
demonstrates color and marker mapping with basic plot elements. The chart
shows correlation between body mass and flipper length for three different
penguin species.

.. bokeh-example-metadata::
    :sampledata: penguins
    :apis: bokeh.plotting.Figure.scatter, bokeh.transform.linear_cmap, bokeh.transform.factor_mark
    :refs: :ref:`userguide_plotting` > :ref:`userguide_plotting_scatter_markers`, :ref:`userguide_data` > :ref:`userguide_data_transforming`
    :keywords: alpha, colormap, markermap, scatter

.. _Palmer penguin dataset: https://github.com/allisonhorst/palmerpenguins
'''
```
This creates the following result:
![image](https://user-images.githubusercontent.com/39711796/138810127-0fda116d-87ce-43d7-8828-aaa83368ada0.png)

user: tcmetzger
issue_11756: state: open
title: [FEATURE] Support for interactive Bokeh visualizations within VS Code (using push_notebook)
body: ### FOLLOW THESE INSTRUCTIONS CAREFULLY

*ISSUES THAT DO NOT CONTAIN NECESSARY INFORMATION MAY BE CLOSED, IMMEDIATELY*

The issue tracker is NOT the place for general support. For questions and technical assistance, come join the [Bokeh Project Discourse](https://discourse.bokeh.org).

**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

**Describe the solution you'd like**
A clear and concise description of what you want to happen.

**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.

**Additional context**
Add any other context or screenshots about the feature request here.

*Note: Everyone interacting in the Bokeh project's codebases, issue trackers, and discussion forums is expected to follow the [Code of Conduct](https://github.com/bokeh/bokeh/blob/master/CODE_OF_CONDUCT.md).*

Hi, I'm one of the maintainers of the [Jupyter extension](https://github.com/microsoft/vscode-jupyter) in VS Code. 
First thanks for this great product, we've found a lot of users use Bokeh within VS Code, and haven't had many issues with this, except for `bokeh server apps` (for which ther'es a separate issue) and `push_notebook` (which is what this issue is about).

I have narrowed down the cause of the issue to the fact the kernels are not accessible in the [notebook.ts](https://github.com/bokeh/bokeh/blob/main/bokehjs/src/lib/embed/notebook.ts) file.
Looking at the code we have special code to handle Jupyter Notebooks, Jupyter Lab and Google Colab.

I was wondering whether you'd be open to a similar special casing for VS Code.

user: DonJayamanne
issue_11747: state: open
title: [BUG] Event loop hooks for server_lifecylce are gone in Bokeh v2.4.1
body: ### Event loop hooks for server_lifecylce are gone in Bokeh v2.4.1

The event loop functions in the [lifecycle-hooks](https://docs.bokeh.org/en/latest/docs/user_guide/server.html#lifecycle-hooks) of the bokeh server are gone in v2.4.1. These following functions on the `server_context` object no longer exist:
- add_periodic_callback
- add_next_tick_callback

In short, this code no longer works (straight from the [docs](https://docs.bokeh.org/en/latest/docs/user_guide/server.html#lifecycle-hooks)):
```python

def foo():
    pass

def bar():
    pass

def on_server_loaded(server_context):
    server_context.add_next_tick_callback(foo)  # this is broken

def on_server_unloaded(server_context):
    server_context.add_next_tick_callback(bar)  # this is broken

def on_session_created(session_context):
    # If present, this function executes when the server creates a session.
    pass

def on_session_destroyed(session_context):
    # If present, this function executes when the server closes a session.
    pass
```

This might just be a missing documentation in the [migration guide](https://docs.bokeh.org/en/latest/docs/releases.html#release-2-4-0-migration), but if you relied on this feature - it now no longer works, so might be nice to document how this can be fixed.

I see that the server_context now has access to an event_loop directly, so maybe that's the way to go?
user: jundl77
issue_11746: state: open
title: [BUG] DataTable autosize_mode = "fit_viewport" does not adjust column widths as data is updated
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
Bokeh 2.4.1
Python 3.9.6
MacOS Catalina 10.15.7
Chrome 94.0.4606.81

#### Description of expected behavior and the observed behavior
When using DataTable's `autosize_mode = "fit_viewport"`, the columns don't get updated when the data or headers change. In applications where the table begins empty and gets filled when the user selects options, this can result in the data looking squished.

That is unlike when using DataTable's `autosize_mode = "fit_columns"`, which updates the column sizes dynamically and is quite nice.

This issue is likely related to #11436.

#### Complete, minimal, self-contained example code that reproduces the issue
Take https://github.com/bokeh/bokeh/tree/branch-3.0/examples/app/export_csv and updated main.py to:
```
from os.path import dirname, join

import pandas as pd

from bokeh.io import curdoc
from bokeh.layouts import column, row
from bokeh.models import (Button, ColumnDataSource, CustomJS, DataTable,
                          NumberFormatter, RangeSlider, TableColumn)

df = pd.read_csv(join(dirname(__file__), 'salary_data.csv'))

source = ColumnDataSource(data=dict())

def update():
    current = df[(df['salary'] >= slider.value[0]) & (df['salary'] <= slider.value[1])].dropna()
    source.data = {
        'name'             : current.name,
        'salary'           : current.salary,
        'years_experience' : current.years_experience,
    }

slider = RangeSlider(title="Max Salary", start=10000, end=110000, value=(10000, 10000), step=1000, format="0,0")
slider.on_change('value', lambda attr, old, new: update())

button = Button(label="Download", button_type="success")
button.js_on_click(CustomJS(args=dict(source=source),
                            code=open(join(dirname(__file__), "download.js")).read()))

columns = [
    TableColumn(field="name", title="Employee Name"),
    TableColumn(field="salary", title="Income", formatter=NumberFormatter(format="$0,0.00")),
    TableColumn(field="years_experience", title="Experience (years)")
]

#data_table = DataTable(source=source, columns=columns, width=800, autosize_mode='fit_columns')
data_table = DataTable(source=source, columns=columns, width=800, autosize_mode='fit_viewport')

controls = column(slider, button)

curdoc().add_root(row(controls, data_table))
curdoc().title = "Export CSV"

update()
```
When the sliders get adjusted such that data appears in the table, the column widths don't get updated.
If you change the DataTable to `autosize_mode='fit_columns'`, the columns width do get updated.

#### Screenshots or screencasts of the bug in action
Squished columns:
<img width="1904" alt="Screen Shot 2021-10-15 at 3 04 13 PM" src="https://user-images.githubusercontent.com/6618019/137539595-9383452d-250b-4662-9c92-d795c185d6c4.png">

user: EfremBraun
issue_11745: state: open
title: Need examples of callbacks with BokehJS
body: ref: https://discourse.bokeh.org/t/bokehjs-callback-on-select-get-start-and-end/8514

There is almost no information I am aware of (examples, docs) demonstrating how BokehJS users might set up callbacks on ranges, selections, etc as there is for Python. The Discourse question above is a prototypical example ask "update a div from a range event"

There should be examples under `bokehjs/examples` and also documentation support in the users guide: https://docs.bokeh.org/en/latest/docs/user_guide/bokehjs.html#
user: bryevdv
issue_11741: state: open
title: Docs: Some links to ref guide broken
body: In the most recent published docs for 2.4, some links to entries in the reference guide are broken (i.e. are rendered as text and are not clickable) because some paths below `bokeh.models` have changed. For example:

In https://docs.bokeh.org/en/2.4.1/docs/user_guide/styling.html#responsive-sizes, `sizing_mode` links to `~bokeh.models.plots.Plot.sizing_mode`, but should now link to `~bokeh.models.Plot.sizing_mode`
![image](https://user-images.githubusercontent.com/39711796/137431210-74504d21-312b-4336-82cc-fb271e16ee97.png)

Or https://docs.bokeh.org/en/2.4.1/docs/user_guide/styling.html#title, where `~bokeh.models.annotations.Title.align` needs to be updated to `~bokeh.models.Title.align`

There are several more instances that need to be checked and updated.
(possibly related to changes from #11563)

user: tcmetzger
issue_11740: state: open
title: [FEATURE] Allow DataTable sorting of NaN values below numeric values
body: **Is your feature request related to a problem? Please describe.**
When NaN or None values appear in a DataTable, they get sorted as if the values are greater than the numeric values. For example:
```
from datetime import date
from random import randint

from bokeh.io import show
from bokeh.models import ColumnDataSource, DataTable, DateFormatter, TableColumn

data = dict(
        dates=[date(2014, 3, i+1) for i in range(11)],
        downloads=[randint(0, 100) for i in range(10)]+[None],
    )
source = ColumnDataSource(data)

columns = [
        TableColumn(field="dates", title="Date", formatter=DateFormatter()),
        TableColumn(field="downloads", title="Downloads")
    ]
data_table = DataTable(source=source, columns=columns, width=400, height=280)

show(data_table)
```
When you sort the Data Table by the Downloads column, the None values gets sorted as if it's greater than the other items.

**Describe the solution you'd like**
I think that usually a None value should be sorted below the numeric values, as it's more commonly something like 0. Alternatively, it can be a user's choice.

**Additional context**
This is really more something that SlickGrid needs to implement before an interface can be built into Bokeh for it. In https://github.com/bokeh/SlickGrid/blob/bokeh%402.4.41/slick.dataview.js, the `comparer` (currently in line 47) is what would need to be changed.
user: EfremBraun
issue_11737: state: open
title: [BUG] Interactivity distorts plot when  background map is added
body: For defects or deficiencies, please provide *ALL OF THE FOLLOWING*:

#### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
versions:
bokeh: 2.4.0
Python: 3.9.7
OS: Windows
Browser: latest Brave (Version 1.30.89 Chromium: 94.0.4606.81 (Official Build) (64-bit)) or Firefox (93.0 (64-bit))

#### Description of expected behavior and the observed behavior

When changing the color through the given widget first, the plot distorts. When first moving the map with the cursor and then changing the color, the plot does not distort. This only happens when a tile background is added. Thus the problem might lay in a difference in the underlaying projection / CRS of the plot and the tile that somehow gets changed in the background (?).

This behaviour is the same in another project of mine when I only activate/deactivate a layer through the legend. It is not limited to the color picker only.

The solution to this problem is `# p.match_aspect = True` (from [here](https://github.com/bokeh/bokeh/issues/4556) but the default behaviour is nonetheless very unexpected and inconsistent.
It's my first bug report to a github project - please excuse possible faults on my side.

#### Complete, minimal, self-contained example code that reproduces the issue
```
from bokeh.layouts import grid
from bokeh.models.widgets.inputs import ColorPicker
from bokeh.sampledata import us_states
from bokeh.plotting import *
from bokeh.tile_providers import get_provider, CARTODBPOSITRON

us_states = us_states.data.copy()

del us_states["HI"]
del us_states["AK"]

# separate latitude and longitude points for the borders
#   of the states.
state_xs = [us_states[code]["lons"] for code in us_states]
state_ys = [us_states[code]["lats"] for code in us_states]

# init figure
p = figure(title="Plotting Points Example: The 5 Largest Cities in Texas", 
           toolbar_location="left", plot_width=1100, plot_height=700)

p.xgrid.grid_line_color = None
p.ygrid.grid_line_color = None
# Draw state lines
p.patches(state_xs, state_ys, fill_alpha=0.0,
    line_color="#884444", line_width=1.5)

#  Latitude and Longitude of 5 Cities
# ------------------------------------
# Austin, TX -------30.26° N, 97.74° W
# Dallas, TX -------32.77° N, 96.79° W
# Fort Worth, TX ---32.75° N, 97.33° W
# Houston, TX ------29.76° N, 95.36° W
# San Antonio, TX --29.42° N, 98.49° W

# Now group these values together into a lists of x (longitude) and y (latitude)
x = [-97.7431, -96.79, -97.33, -95.36, -98.49]
y = [30.26, 32.77, 32.75, 29.76, 29.42] 

# add basemap and labels
tile_provider = get_provider(CARTODBPOSITRON)
p.add_tile(tile_provider)
points = p.circle(x, y, size=8, color='navy', alpha=1)

# p.match_aspect = True

picker = ColorPicker(title=f"Point Color", color="navy")
picker.js_link("color", points.glyph, "fill_color")

# output to static HTML file
output_file("texas.html")
l = grid([p, picker], ncols=2, sizing_mode="fixed")

# show results
# show(l)
show(column([p, picker]))
```

#### Screenshots or screencasts of the bug in action
undistorted:
![image](https://user-images.githubusercontent.com/73953672/137093347-3099e5ca-6a14-4ea1-a278-febad46b5953.png)

after changing the color of the points:
![image](https://user-images.githubusercontent.com/73953672/137093509-5c074382-991e-4d30-9a0f-6b42c3c6fde2.png)
user: BoSott
issue_11735: state: open
title: [BUG] Cannot update Array property on DataModel
body: When using `DataModel` in a server context you are meant to be able to update properties, however currently an `Array` property cannot be updated:

```python
import numpy as np
import panel as pn

from bokeh.core.properties import Float, Array, Any
from bokeh.io import save
from bokeh.model import DataModel
from bokeh.models import ColumnDataSource, CustomJS
from bokeh.plotting import figure

class Params(DataModel):
    amp = Float(default=0.1, help="Amplitude")
    freq = Float(default=0.1, help="Frequency")
    phase = Float(default=0, help="Phase")
    offset = Float(default=-5, help="Offset")
    random = Array(Any, default=np.random.rand(100))

params = Params(amp=2, freq=3, phase=0.4, offset=1, random=np.random.rand(100))

A = params.amp
k = params.freq
phi = params.phase
B = params.offset
x = np.linspace(0, 10, 100)
y = A*np.sin(k*x + phi) + B + params.random

source = ColumnDataSource(data=dict(x=x, y=y))

plot = figure(tags=[params], y_range=(-10, 10), title="Data models example")
plot.line("x", "y", source=source, line_width=3, line_alpha=0.6)

callback = CustomJS(args=dict(source=source, params=params), code="""
    const data = source.data
    const A = params.amp
    const k = params.freq
    const phi = params.phase
    const B = params.offset
    const {x, y} = data
    for (let i = 0; i < x.length; i++) {
        y[i] = A*Math.sin(k*x[i] + phi) + B + params.random[i]
    }
    source.change.emit()
""")

params.js_on_change("amp", callback)
params.js_on_change("freq", callback)
params.js_on_change("phase", callback)
params.js_on_change("offset", callback)
params.js_on_change("random", callback)

pn.panel(plot)
```

Now if we update this example from the server:

```python
params.random = np.random.rand(100)
pn.io.push_notebook(bk_pane)
```

We get this error:

```
~/miniconda3/envs/dev3.8/lib/python3.8/site-packages/bokeh/document/events.py in generate(self, references, buffers)
    441         # we know we don't want a whole new copy of the obj we're patching
    442         # unless it's also the new value
--> 443         if self.model != value:
    444             value_refs.discard(self.model)
    445         references.update(value_refs)

ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
```

This is because comparing the array using a simple equality does not work. Would it make sense to change that to?

```python
if isinstance(value, Model) and self.model != value:
```
user: philippjfr
issue_11718: state: open
title: Revise initial value logic in `DataRange1d`
body: See PR https://github.com/bokeh/bokeh/pull/11717.

user: mattpap
issue_11666: state: open
title: Documentation should have more robust support for permalinks
body: Currently only top-level sections have permalinks, which is insufficient when dealing with complex topics. For example, here https://github.com/bokeh/bokeh/pull/11663#issuecomment-924700399 I had to point a contributor to a documentation page and then reference section title text. Hopefully permalinks can be added to subsections, individual points and notes.

\cc @tcmetzger 

user: mattpap
issue_11651: state: open
title: Use `scale` in `ImageURLTexture` to resize images
body: Currently it's being unused. It seems to me it would make sense to resize requested images accordingly. Though I'm not sure what to do about backwards compatibility as the default scale is `12px`. Perhaps a new default value `"auto"` would have to be added.

user: mattpap
issue_11647: state: open
title: [FEATURE] Remove toolbar from png on export_png()
body: This issue is mentioned at https://github.com/holoviz/holoviews/issues/3751, but I think it's more relevant to Bokeh than to HoloViews.

When I try to export a plot using `export_png()`, the toolbar gets included with it. `export_svg()` does not include the toolbar (which is the desired response, since having a non-functioning toolbar on a static image isn't helpful).

The behavior can be seen with the following (minor modifications to https://docs.bokeh.org/en/latest/docs/gallery/marker_map.html):
```
from bokeh.plotting import figure, show
from bokeh.sampledata.penguins import data
from bokeh.transform import factor_cmap, factor_mark
from bokeh.io import export_png, export_svg

SPECIES = sorted(data.species.unique())
MARKERS = ['hex', 'circle_x', 'triangle']

p = figure(title = "Penguin size", background_fill_color="#fafafa")
p.xaxis.axis_label = 'Flipper Length (mm)'
p.yaxis.axis_label = 'Body Mass (g)'

p.scatter("flipper_length_mm", "body_mass_g", source=data,
          legend_group="species", fill_alpha=0.4, size=12,
          marker=factor_mark('species', MARKERS, SPECIES),
          color=factor_cmap('species', 'Category10_3', SPECIES))

p.legend.location = "top_left"
p.legend.title = "Species"

export_png(p, filename='example.png')
export_svg(p, filename='example.svg')
```
user: EfremBraun
issue_11641: state: open
title: [FEATURE] Expanded math text for tick labels
body: The current support for math text for tick labels is great for fairly static plots, but the need to specify labels only via tick label overrides is limiting when it is desirable that the plot can be panned or zoomed. I suspect that the majority of cases where mathtext is desired on a tick label is only for specifying a unit of some sort (e.g. Ω or *c^2*, etc). So I would like to propose adding a mathtext-enabled property, available to all tick formatters, that simply specifies a fixed suffix or unit to append to the formatted numerical value. E.g. 

```python
p.xaxis.formatter.unit = r"$$\ohm$$"    

# or

p.xaxis.formatter.suffix = r"$$\ohm$$"
```
Or maybe there are other better property names. 

I like this approach because to should be straighforward to implement for all numerical tick formatters, and I think that it would cover the most common need. I also think that it is more flexible to add this capability to all tick formatters since it  allows composition of the formatting of the number in any way desired (printf, numbro, functick) independently with the suffix/unit.

The alternative would be a new model like `MathTextTickFormatter` which either:

* locks users in to a single number formatter (whatever we happen to use), or 
* forces an awkard `.axis.formatter.formatter` sub-formatter sitatuation that is more work for user, and more work for us (and mixing numbro or printf formatting in the same string as latex markup may even be problematic) .

cc @bokeh/dev 
user: bryevdv
labels: tag: MathText: Related to MathText (TeX/MathML/AsciiMath)
issue_11619: state: open
title: Cannot find my extension widget js after upgrading to 2.4.0
body: I built my extension widgets following the example in the bokeh repository (custom/wrapped_extension_full).
It worked fine in 2.3.3. The browser could fetch my js extension file successfully under the URL /static/extensions/ .
After I upgraded to 2.4.0, I issued the bokeh command to rebuild it. The js extension file was built successfully in the usual dist folder, but the browser no longer could fetch it.

Any changes I missed about the extension path in 2.4.0?
user: maeglin89273
issue_11608: state: open
title: [BUG] clicking on toolbar/widgets highlights
body: cc @mattpap FYI seeing a weird "halo" highlight around some widgets and toolbar buttons when they are interacted with (this may only be safari tho, triaging that way for now at least) 


<img width="195" alt="Screen Shot 2021-09-14 at 10 07 21" src="https://user-images.githubusercontent.com/1078448/133302528-1e0f959d-57bd-4935-ac7a-ee5d5513d2b1.png">

user: bryevdv
issue_11602: state: open
title: Provide window resize event
body: **Is your feature request related to a problem? Please describe.**
In order to provide a python based reflow we need a window size event that can be hooked to a callback the manipulates the layouts (example horizontal versus vertical).

This has been discussed here:
https://discourse.bokeh.org/t/responsive-layout-with-reflow-for-screen-rotations/8219/23

**A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]**
For the sake of concern separation we would like to keep the layout logic on Bokeh and allow the switch between horizontal and vertical modes directly on the python code.

**Describe the solution you'd like**
A Bokeh WindowResized event that can be connected to a python callback.

**Describe alternatives you've considered**
The alternative is inserting individual plots - rather than grid - into the HTML, forcing the user to understand the complexities of HTML reflows.

**Additional context**
Add any other context or screenshots about the feature request here.

This vertical layout, from an otherwise horizontal website, has been achieved thanks to the DocumentReady event. With a WindowResized event we would achieve complete responsiveness.

![Screenshot_20210914_015340](https://user-images.githubusercontent.com/3352900/133176199-ea6764db-a7c6-4f21-a446-7c342af1dcc8.png)


More information here:

https://discourse.bokeh.org/t/responsive-layout-with-reflow-for-screen-rotations/8219/23
user: ghomem
issue_11601: state: open
title: Be more careful with mutated sample data in docs examples
body: Currently, the `:bokeh-plot:` sphinx directive runs all examples in a given docs build run in a single process. If the examples modify mutable data sources from `bokeh.sampledata` this can cause subtle and order-dependent build failures. (ref: https://github.com/bokeh/bokeh/pull/11599) There are a couple of possible ways to mitigate this:

* always return a copy from `bokeh.sampledata` e.g. perhaps with a module-getattr
* run docs example code in separate subprocesses

Or possibly both. 
user: bryevdv
issue_11594: state: open
title: HTML annotations bound to a frame don't support clipping
body: ![image](https://user-images.githubusercontent.com/27475/132980176-94575b2f-0b67-4c0e-9700-76b1e88f71b0.png)

user: mattpap
issue_11593: state: open
title: [FEATURE] Support "date" axes with gaps (e.g. skip weekends)
body: **Is your feature request related to a problem? Please describe.**
When plotting time series data with gaps in date time, bokeh will automatically fill the gap with. empty spaces. (this is not a bug, nothing wrong here). For example, in this [candlestick](https://docs.bokeh.org/en/latest/docs/gallery/candlestick.html) plot, you can see a lot of empty spaces due to weekends or holidays. In a lot of situation, time series charts would look better without those empty spaces. The current solution is plot data with their integer row numbers and use major_label_overrides to override the labels, for example, check [here](https://stackoverflow.com/a/23585653/884553) for the same charts without gap. BUT, when running a bokeh server, it's much easier and efficient to be able to point this to a data source, so those labels won't be replot every time data source updates. (I don't have a deep understanding of bokeh, but I think bokeh only re-plots the deltas / changes every time data source updates). 

**Describe the solution you'd like**
Enable setting major_label_overrides to a data source, such as:

p.set_from_source("major_label_overrides", "name_of_datetime_column", source=source)

And source["name_of_datetime_column"] is list of datetime (not dict), must has the same size as major ticks

**Describe alternatives you've considered**
As mentioned [here](https://stackoverflow.com/questions/69120135/how-to-neglect-weekends-data-in-bokeh-time-series-plot-running-on-bokeh-server?noredirect=1#comment122213672_69120135) in the discussion, when running a bokeh server, one may set call backs on range change to set major_label_overrides to a new dictionary, but i think this will trigger replot of the whole labels. not efficient for bokeh server.

user: gengjun
issue_11557: state: open
title: [BUG] DataTable's autosize_mode breaks when string data in source is empty
body: 
#### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
Bokeh 2.3.3 (though the issue also shows up using 2.4.0dev4+11.g03cc08c31)
Python 3.8.5
MacOs Catalina 10.15.7
Google Chrome 92.0.4515.159

#### Description of expected behavior and the observed behavior
When a `Datatable` uses `autosize_mode`, and the data in the table becomes empty due to user interaction, an error is thrown which prevents certain types of user interaction.

I'm only seeing this when at least one of the columns in the `Datatable` contains strings. If all columns are numbers, this does not appear to be an issue.

#### Complete, minimal, self-contained example code that reproduces the issue
Based on the example given in https://github.com/bokeh/bokeh/tree/branch-2.4/examples/app/export_csv:
Here is the revised `main.py` code. `download.js`, `salary_data.csv`, and `templates/index.html` should be copy/pasted to the working directory directly from the example.

```
from os.path import dirname, join
import random
import string

import pandas as pd

from bokeh.io import curdoc
from bokeh.layouts import column, row
from bokeh.models import (Button, ColumnDataSource, CustomJS, DataTable,
                          NumberFormatter, RangeSlider, TableColumn, Div)

df = pd.read_csv(join(dirname(__file__), 'salary_data.csv'))

source = ColumnDataSource(data=dict())

def update():
    current = df[(df['salary'] >= slider.value[0]) & (df['salary'] <= slider.value[1])].dropna()
    source.data = {
        'name'             : current.name,
        'salary'           : current.salary,
        'years_experience' : current.years_experience,
    }

def changeLabel():
    div.text = ''.join(random.choices(string.ascii_uppercase + string.digits, k=5))

slider = RangeSlider(title="Max Salary", start=10000, end=110000, value=(10000, 50000), step=1000, format="0,0")
slider.on_change('value', lambda attr, old, new: update())

button = Button(label="Change div text", button_type="success")
button.on_click(changeLabel)
div = Div(text='Initial text')

columns = [
    TableColumn(field="name", title="Employee Name"),
    TableColumn(field="salary", title="Income", formatter=NumberFormatter(format="$0,0.00")),
    TableColumn(field="years_experience", title="Experience (years)")
]

data_table = DataTable(source=source, columns=columns, width=800, autosize_mode='fit_columns')

controls = column(slider, button, div)

curdoc().add_root(row(controls, data_table))
curdoc().title = "Export CSV"

update()
```


#### Stack traceback and/or browser JavaScript console output
Start the session, move the range slider to 50,000-50,000 such that no data is in the table, and I see this error shown in the console:
```
bokeh-tables.min.js?v=6fb012492967979769943f06b5e61af63c036c660b84615c8e08f828dfb203c82a2a86dd976666f31d02b3792b63c63cfd15568369da1b9712b973c8c9f5e7e8:106 Uncaught TypeError: Cannot read property 'ions' of undefined
    at getColContentSize (bokeh-tables.min.js?v=6fb012492967979769943f06b5e61af63c036c660b84615c8e08f828dfb203c82a2a86dd976666f31d02b3792b63c63cfd15568369da1b9712b973c8c9f5e7e8:106)
    at getColAutosizeWidth (bokeh-tables.min.js?v=6fb012492967979769943f06b5e61af63c036c660b84615c8e08f828dfb203c82a2a86dd976666f31d02b3792b63c63cfd15568369da1b9712b973c8c9f5e7e8:106)
    at SlickGrid.autosizeColumns (bokeh-tables.min.js?v=6fb012492967979769943f06b5e61af63c036c660b84615c8e08f828dfb203c82a2a86dd976666f31d02b3792b63c63cfd15568369da1b9712b973c8c9f5e7e8:106)
    at M.updateLayout (bokeh-tables.min.js?v=6fb012492967979769943f06b5e61af63c036c660b84615c8e08f828dfb203c82a2a86dd976666f31d02b3792b63c63cfd15568369da1b9712b973c8c9f5e7e8:55)
    at M.updateGrid (bokeh-tables.min.js?v=6fb012492967979769943f06b5e61af63c036c660b84615c8e08f828dfb203c82a2a86dd976666f31d02b3792b63c63cfd15568369da1b9712b973c8c9f5e7e8:55)
    at M.<anonymous> (bokeh-tables.min.js?v=6fb012492967979769943f06b5e61af63c036c660b84615c8e08f828dfb203c82a2a86dd976666f31d02b3792b63c63cfd15568369da1b9712b973c8c9f5e7e8:55)
    at M.s (bokeh.min.js?v=144cb36389ca2d32825eb47dac89e791731d09f40c4bdcb3be3b654b99b021edb4a552f55f813236e3f751b6f457c846b4bcb0b68abea1aab190bf15d62c3b31:207)
    at r.emit (bokeh.min.js?v=144cb36389ca2d32825eb47dac89e791731d09f40c4bdcb3be3b654b99b021edb4a552f55f813236e3f751b6f457c846b4bcb0b68abea1aab190bf15d62c3b31:180)
    at r.emit (bokeh.min.js?v=144cb36389ca2d32825eb47dac89e791731d09f40c4bdcb3be3b654b99b021edb4a552f55f813236e3f751b6f457c846b4bcb0b68abea1aab190bf15d62c3b31:180)
    at p._setv (bokeh.min.js?v=144cb36389ca2d32825eb47dac89e791731d09f40c4bdcb3be3b654b99b021edb4a552f55f813236e3f751b6f457c846b4bcb0b68abea1aab190bf15d62c3b31:179)
```
Now, click the `Change div text` button, and the same error will appear. The button will work though. However, if you now click the `Change div text` button again, the button will not work (no error gets shown though). Now, even if you change the range slider such that data appears in the table, it won't matter; the `Change div text` button is permanently broken.
user: EfremBraun
issue_11549: state: open
title: Tile rendering tests or readiness notification logic are unrobust
body: This happens with some regularity:
```
Bug
 └┬─ in issue #11413
  └── doesn't allow to remove an annotation element associated with a tile renderer
timeout
```
user: mattpap
issue_11545: state: open
title: [BUG] Properties `transform` are not applied when inside `Dict` items.
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
bokeh: 2.4.0dev6+2.gd3fce050d
Python 3.8.10
MacOS 11.5

#### Description of expected behavior and the observed behavior

When setting a `transform` method for a property, it is expected that the value set to this property will go through this method. Currently, if you set a Dict, the keys and values only go through the `validate` method.

#### Complete, minimal, self-contained example code that reproduces the issue

```python
p = figure()
p.xaxis.axis_label = r"$\Omega$"
assert isinstance(p.xaxis.axis_label, MathText) # True

p.xaxis.ticker = FixedTicker(ticks=[1,2,3,4])
p.xaxis.major_label_overrides = {
    1: r"$1\Omega$",
    2: r"$2\Omega$",
    3: r"$3\Omega$",
    4: r"$4\Omega$",
}
assert isinstance(p.xaxis.major_label_overrides[1], MathText) # False

```


user: IuryPiva
issue_11541: state: open
title: [BUG] Regression in layout rendering (since v2.2.3)
body: #### Software versions:
bokeh - 2.3.3
python - 3.9.6
browser - Chromium - 92.0.4515.159 and/or Falkon 3.1.0 based on QtWebEngine 5.15.2

#### Description of expected behavior and the observed behavior
The issue description is copied from [https://discourse.bokeh.org/t/update-on-layout-regression-in-2-3-2](https://discourse.bokeh.org/t/update-on-layout-regression-in-2-3-2).

#### Complete, minimal, self-contained example code that reproduces the issue
The basic server app can look like this:

```
from bokeh.plotting import figure, curdoc
from bokeh.layouts  import gridplot

class Layout:
    def __init__(self):
        self.layout = gridplot(
            [
                [figure()],
                [figure()],
            ],
            sizing_mode='stretch_both',
        )
        curdoc().add_root(self.layout)

Layout()
```

Then, without any template it renders as expected:

![image](https://user-images.githubusercontent.com/15859701/131512654-818020ec-086f-445c-a804-a91ce4f4fb10.png)

However with basic template like this:

```
{% extends base %}

{% block contents %}

<div>
    {{ embed(roots[0]) }}
</div>

{% endblock %}
```

It renders like this:

![image](https://user-images.githubusercontent.com/15859701/131513122-f6e05a28-118b-43cf-8e51-19b391318f89.png)

I can make it work by removing the div element, however as long as there are any other divs, not necessary around the embeded content (which there are in my real app) it does not help.
user: rad83
issue_11529: state: open
title: Upgrade numbro to version 2.x
body: **Note:** Posting this here since the forked version of `numbro` at [bokeh/numbro](https://github.com/bokeh/numbro) does not have its own issue tracker.

#### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
Bokeh: 2.3.3
Numbro: 1.6.2

The bundled fork of `numbro` is *very* outdated (seems like it's almost 7 years and over 330 commits behind the upstream), and as such, it's lacking a lot of fixes and features that have since been added, including the fact it cannot properly format negative byte numbers, which is biting us currently. 

Is there a reason Bokeh forks it and publishes own version? The upstream has since seen a from-scratch rewrite in version 2.x, but it seems like it still supports all of the old format language. Changing to a dependency on the upstream package (possibly pinned to a particular version if necessary) should make it possible to take advantage of all the work that's been happening there.
user: mathrick
issue_11524: state: open
title: [FEATURE] Add ability to align RadioGroup options with icons
body: As discussed in https://discourse.bokeh.org/t/radiogroup-appearance/7151:

It would be nice to have control over the spacing between options in a RadioGroup so that the options can be aligned with other widgets, e.g., icons.
user: EfremBraun
issue_11504: state: open
title: [BUG] Spinner widget value reverts to low/high
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
Bokeh 2.3.3
Python 3.8.5
MacOs Catalina 10.15.7
Google Chrome 92.0.4515.131

#### Description of expected behavior and the observed behavior
If one tries to change the `value` and `low` parameters of a Spinner widget, the `value` takes on the new `value` at first, but then later it reverts to what was set as the `low` parameter. Similar strange things occur when trying to change the `value` and `high` parameters.

#### Complete, minimal, self-contained example code that reproduces the issue

```
from bokeh.io import curdoc
from bokeh.layouts import column
from bokeh.models import Spinner, Button

spinner = Spinner(low=1, value=2)
button1 = Button(label='Change')
button2 = Button(label='Print Again')

def change_spinner():
    spinner.low = 5
    spinner.value = 50
    print(spinner.value)

def print_spinner():
    print(spinner.value)

button1.on_click(change_spinner)
button2.on_click(print_spinner)

curdoc().add_root(column(spinner, button1, button2))
```

#### Stack traceback and/or browser JavaScript console output
After clicking the "Change" button, the console prints 50.
After then clicking the "Print" button, the console prints 5 (though it should print 50).

#### Complete, minimal, self-contained example code that reproduces the issue

```
from bokeh.io import curdoc
from bokeh.layouts import column
from bokeh.models import Spinner, Button

spinner = Spinner(high=100, value=2)
button1 = Button(label='Change')
button2 = Button(label='Print Again')

def change_spinner():
    spinner.high = 70
    spinner.value = 50
    print(spinner.value)

def print_spinner():
    print(spinner.value)

button1.on_click(change_spinner)
button2.on_click(print_spinner)

curdoc().add_root(column(spinner, button1, button2))
```

#### Stack traceback and/or browser JavaScript console output
After clicking the "Change" button, the console prints 50.
After then clicking the "Print" button, the console prints 2 (though it should print 50).
user: EfremBraun
issue_11481: state: open
title: Improve usability with color vision deficiencies
body: As discussed in https://github.com/bokeh/bokeh/issues/10974, there are several aspects of Bokeh that should be improved to make Bokeh more usable to people with color vision deficiencies:

- [ ] Update the colors/palettes used in Bokeh's documentation, examples, and gallery
- [ ] Review the [palette section in the reference guide](https://docs.bokeh.org/en/latest/docs/reference/palettes.html?highlight=palettes#module-bokeh.palettes) and add more palettes and information on how to accommodate differences in color-vision 
- [ ] Review all of Bokeh's assets (website, docs, etc) and update colors/information where necessary
user: tcmetzger
issue_11476: state: open
title: Improvements to accepts clauses for properties
body: As noted in https://github.com/bokeh/bokeh/pull/11466#issuecomment-891948204 the current codepath for `accepts` has some deficiencies:

https://github.com/bokeh/bokeh/blob/9bfa131130da3a6905654b1f9f4388c87f1f91b0/bokeh/core/property/bases.py#L344-L353

Specifically:

* Current `accepts` only works in the presence of full validation
* Current `accepts` can only be a fallback if initial validation fails

There are two goals to support in this issue:

#### Afford "pre" alternatives 

Some cases would like to apply a conversion *up front*, e.g. checking if a String is specifically a "Math String" 

This could be done two ways:

* add a flag to `accepts` 
* add a new `converts` method for the "pre" case

I think I would personally go for the latter and have `.accepts` be mean "accepted as a fallback" and `.converts` mean "converted before use". Separately we might choose to change many or all of the existing `accepts` clauses to `converts` clauses.

#### Fix validation interaction

Currently `accepts` only works when validation is enabled (see code above). We absolutely want to keep the promise of no expensive full-value validation when validation is turned off explicitly. I think even to the point of documenting that `accepts` and `converts` cannot be relied on if validation is turned off, if that's what it comes to. 

But perhaps we could do better, e.g. by adding having `.convert` be able to do some quick-path validations of some sort. Another possibility is that we might have to explicitly identify / curate expensive validations in some way, and disallow converts/accepts on these types. 

cc @IuryPiva @mattpap @bokeh/dev

user: bryevdv
issue_11461: state: open
title: [FEATURE] where there are multiple related properties that might need to be responded to as a group, respond to them only after all are guaranteed to be updated
body: **Is your feature request related to a problem? Please describe.**
When multiple properties of a widget change (e.g., when the user selects a file from the FileInput widget, both the 'filename' and 'value' properties get updated), the developer may see a callback that responds to a change of property called before the other property has been updated.

**Describe the solution you'd like**
The case where there are multiple related properties that might need to be responded to as a group, is not the common case with Bokeh. Most properties are “standalone” in that sense. But there are some cases, e.g. wanting to respond to a consolidated range update instead of separate start and end updates separately, or FileInput widgets where it is reasonable to want to respond to both value and filename together only after they are both guaranteed to be updated.

**Describe alternatives you've considered**
The developer can set the callback to the property that is last updated (e.g., the filename for FileInput widgets), but the docs should inform the developers which property this is.

**Additional context**
See https://discourse.bokeh.org/t/fileinput-widgets-callback-on-value-parameter-comes-before-filename-is-set/8209 for more discussion.
user: EfremBraun
issue_11455: state: open
title: [FEATURE] Horizontal tooltips
body: **Is your feature request related to a problem? Please describe.**
No

**Describe the solution you'd like**
When hovering over multiple glyphs, there will be displayed multiple tooltips in vertical manner. Would be nice if we would be able to choose their alignment(vertical or horizontal). 

Even more, in the future, an option could be added to display them in a grid, like 3x3, but for now horizontal alignment should be enough

user: Moldoteck
issue_11440: state: open
title: [FEATURE] Restrict copying to the selected cell, not the row
body: ### Problem statement

In its current functioning, selecting a cell from a DataTable then copying & pasting will copy the full row rather than the selected cell.
[It seems to be because rows attached to the `indices` JS object is what is actually being copied](https://discourse.bokeh.org/t/copy-and-paste-cell-value/8166/2)

This is not always wished, as the active cell is clearly visible and only its content may need to be copied.

### ideal solution
Ideally, this should be a parameter when instantiating the table.
IMHO, Keeping the current behavior by default would be OK. 

### alternative solution

Alternatively, there could be an object corresponding to the cell tagged as `active` in a CustomJS callback attached to `cb_obj`.
I believe it can then be manipulated using [ClipboardEvent](https://developer.mozilla.org/en-US/docs/Web/API/ClipboardEvent/clipboardData), although I have no experience with this.

user: hyamanieu
issue_11439: state: open
title: [BUG] JS callback not working consistently with IndexFilter
body: For defects or deficiencies, please provide *ALL OF THE FOLLOWING*:

#### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
* Python 3.9.6
* Bokeh 2.3.3
* Mac 10.14.1
* Brave 1.23.71 Chromium: 90.0.4430.72 (Official Build) (x86_64)

#### Description of expected behavior and the observed behavior
* Expected: Animating a single object in a plot with a `view` and a `customJS` callback updates the object for all frames.
* Observed: After moving forward some number of frames, the object stops moving.

There's some more info about it on [the discourse](https://discourse.bokeh.org/t/js-callback-not-working-consistently-with-indexfilter/8160).

#### Complete, minimal, self-contained example code that reproduces the issue

```
import pandas as pd
from bokeh.layouts import column
from bokeh.models import ColumnDataSource, CDSView, CustomJS, IndexFilter, Slider
from bokeh.plotting import figure, show

source = ColumnDataSource(pd.read_csv("test.csv"))
filter = IndexFilter([0])

plot = figure(height=200, x_range=(60, 105), y_range=(0, 30))
plot.circle(x="x", y="y", source=source, 
            view=CDSView(source=source, filters=[filter]))

slider = Slider(start=0, end=60, value=0, step=1)

callback = CustomJS(args={"source": source, "filter": filter}, code="""
    filter.indices = [cb_obj.value];
    source.change.emit();
""")
slider.js_on_change("value", callback)

show(column(plot, slider))
```
The test.csv file referenced in the code [is here](https://github.com/bokeh/bokeh/files/6851672/test.csv).


#### Stack traceback and/or browser JavaScript console output
Weirdly there isn't any, I was really expecting to see something in the Chrome console but there aren't any errors.

#### Screenshots or screencasts of the bug in action
I made an animated gif of the behavior (sorry for the relatively poor quality):

![bokeh_glitch](https://user-images.githubusercontent.com/923682/126406028-db1cd59a-3df8-4cd0-a521-230f508b79d4.gif)

*Note: Everyone interacting in the Bokeh project's codebases, issue trackers, and discussion forums is expected to follow the [Code of Conduct](https://github.com/bokeh/bokeh/blob/master/CODE_OF_CONDUCT.md).*

user: AndrewRook
issue_11436: state: open
title: [BUG] DataTable doesn't fit column to rounded number
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)

- Bokeh 2.3.3
- Python 3.6.6

#### Description of expected behavior and the observed behavior

I have a DataTable with autosize_mode set to fit_viewport. One of the columns has a NumberFormatter with format set to 2 decimal places. When supplying a number with more than 2 decimals places the column width is calculated based on the number with all decimals and not the shorter one. Instead of a small column  width we are left with a wide one that has a  lot of whitespace.

#### Complete, minimal, self-contained example code that reproduces the issue

```
import bokeh.models
import bokeh.plotting


doc = bokeh.plotting.curdoc()

source = bokeh.models.ColumnDataSource(data=dict(
    text=["something"],
    number=[0.333333333333333333],
    other_number=[12345]
))

columns = [
    bokeh.models.TableColumn(
        field="text",
        title="Text"),
    bokeh.models.TableColumn(
        field="number",
        title="Number",
        formatter=bokeh.models.NumberFormatter(format=".00")),
    bokeh.models.TableColumn(
        field="other_number",
        title="Other number")
]

table = bokeh.models.DataTable(
    source=source,
    columns=columns,
    autosize_mode="fit_viewport"
)

doc.add_root(table)

```

#### Stack traceback and/or browser JavaScript console output

#### Screenshots or screencasts of the bug in action
![image](https://user-images.githubusercontent.com/2953645/126364287-8f12faeb-6215-4f6c-a2bf-e71b6c11deea.png)


user: Kadek
issue_11434: state: open
title: DataTable with 100K rows hangs on column operations
body: Using bokeh 2.3.3 and jupyter_bokeh 3.0.2 in a jupyterlab 3 notebook.  
Run the following cell and click on the "select all" checkbox or any of the column headers.   
The jupyter client hangs.

```
from jupyter_bokeh.widgets import BokehModel
import pandas as pd
from bokeh.models import ColumnDataSource, DataTable, TableColumn
from bokeh.io import output_notebook
output_notebook()
idx = list(range(100000))
data_dict = dict( index= idx, value= [ str(id) for id in idx ] )
df = pd.DataFrame(data_dict)
tab_columns = [ TableColumn(field=colname, title=f'{colname}') for colname in data_dict.keys() ]
cds = ColumnDataSource(df)
table = DataTable( source=cds, columns=tab_columns, width=600, height=150, sortable=True, selectable="checkbox" )
BokehModel( table )
```

user: nasa-nccs-cds
issue_11429: state: open
title: Chart not resize correctly on windows resize
body: Hi all,
I'm  using bokeh charts with python Flask to create a dashboard.

I set the chart sizing_mode="stretch_both" to fill the parent div.  It fills the parent div dimension correctly on loading.   

But when I double click to change the browser window size, the chart does not resize correctly.  Sometimes it shrink in height.  I need to switch back and forth between tabs for the chart the resize correctly.   I can also slightly drag to resize the window and the chart would resize to the correct height to fit the parent div height.
  
See picture below:

![image](https://user-images.githubusercontent.com/63297801/126196309-5e2030b3-fef7-4e58-b57b-ca49516a4cb4.png)

Is there anyway to force a resize or update size event for all the charts to fill totheir parent container height?
user: iisri-vu
issue_11425: state: open
title: [BUG] Can't find `visuals` folder after updating to latest Bokeh version
body: Greetings from SciPy 2021 sprint :wave: 

I hit the following error in the browser after updating from `branch-2.3` to `branch-2.4`:
```
Uncaught ReferenceError: Bokeh is not defined
```
I don't have the full traceback anymore, but IIRC it was about the `visuals` folder that was missing.
We solved it by removing the `bokehjs/build` folder and rebuilding BokehJS. Maybe this should be mentioned in the docs somehow?

user: kir0ul
issue_11403: state: open
title: Add regression tests after PR #11394
body: - [x] #11344
- [x] #11188
- [x] #11161 
- [x] #11083
- [ ] #11057
- [ ] #10851
user: mattpap
issue_11396: state: open
title: [BUG] Column max_height appears to be ignored
body: ### FOLLOW THESE INSTRUCTIONS CAREFULLY

*ISSUES THAT DO NOT CONTAIN NECESSARY INFORMATION MAY BE CLOSED, IMMEDIATELY*

The issue tracker is NOT the place for general support. For questions and technical assistance, come join the [Bokeh  Project Discourse](https://discourse.bokeh.org).

For defects or deficiencies, please provide *ALL OF THE FOLLOWING*:

#### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
Python 3.9.4
bokeh 2.3.2
Windows 10
Microsoft Edge, Opera
#### Description of expected behavior and the observed behavior

Expectation that setting max_height of the column should limit the height of the column.

From the example, it appears as though max_width of the column is respected but max_height is not. None of the inner figures for the column have any fixed heights or aspect ratio set are set to take any available space. 

#### Complete, minimal, self-contained example code that reproduces the issue

```
from bokeh.plotting import figure, show
from bokeh.layouts import column
import numpy as np

fig1 = figure(sizing_mode="stretch_both")
fig1.line(x=np.arange(100), y=np.ones(shape=(100)))
fig2 = figure(sizing_mode="stretch_both")
fig2.line(x=np.arange(100), y=np.ones(shape=(100))*5)
col = column([fig1, fig2] , sizing_mode="stretch_both", max_width=500, max_height=200)
show(col)
```

#### Stack traceback and/or browser JavaScript console output

#### Screenshots or screencasts of the bug in action

*Note: Everyone interacting in the Bokeh project's codebases, issue trackers, and discussion forums is expected to follow the [Code of Conduct](https://github.com/bokeh/bokeh/blob/master/CODE_OF_CONDUCT.md).*

user: nss350
issue_11389: state: open
title: [FEATURE] Enable Tooltips on Label or Title
body: Overall problem: 
Trying to have an informative description (HTML text) about a plot to pop up on the plot when hovering over a label 

**Is your feature request related to a problem? Please describe.**
I'm currently trying to have a Label fixed on the top right corner of a plot (displaying a question mark), that when I hover over it, it displays a description about the plot. 
I know that the toolbar `?` can be use to link to a description of the plot, but I want the description to be on the plot not on an external link. But currently I can't link a HoverTool to a Label. 


```python
from bokeh.plotting import figure, show
from bokeh.models import Label,  HoverTool

x = [1, 2, 3, 4, 5, 6]
y = [5, 3, 7, 7, 1, 2]

p = figure(plot_width=600, plot_height=600)
p.line(x=x, y=y)

my_label = Label(x=p.plot_width - 70, y=p.plot_height-50, x_units="screen", y_units="screen", text="?")

p.add_layout(my_label)

hover = HoverTool(
            point_policy="follow_mouse",
            tooltips="""Your <a href="https://en.wikipedia.org/wiki/HTML">HTML</a>-supported text is 
initialized with the <b>text</b> argument.  The remaining div arguments are <b>width</b> and <b>height</b>. For
this example, those values are <i>200</i> and <i>100</i>, respectively.""",
            renderers=[my_label],
        )

p.add_tools(hover)

show(p)
```
```
ValueError: failed to validate HoverTool(id='4921', ...).renderers: expected an element of either Auto or List(Instance(DataRenderer)), got [Label(id='4920', ...)]
```

**Describe the solution you'd like**
I would like to be able to have a question mark displaying on the top right corner of a plot ( using `x_units` and `y_units`) that when I hover over it it provide a description of the plot in HTML text. 

**Describe alternatives you've considered**

- I though of displaying a glyph or ImageURL below the label and render the hover on it, but none of the glyphs provide `x_units` or `y_units` which is a problem, given that the plots are on a dashboard that change sizes and I want the Label/glyph to always be on the same place (top right corner of the plot)


**Additional context**
-(1) I was able to have a button that when clicked it display HTML but neither the button or the text are in the plot, but on the side (see image below). If there is a chance of having the button right next to the title TESTING (on the right side not left) and the HTML text to display over the button when clicked (not outside the plot area), this could be an alternative. 
- (2) Another possible option would be to be able to have a hover tooltip to display on the Title object, but hovers currently don't display on Title.

code for (1)
```python

from bokeh.plotting import figure, show, output_notebook
from bokeh.models import Label, Button, CustomJS, Title, Div, HoverTool
from bokeh.layouts import column, row

p = figure()

title_test = Title(text="TESTING", text_font_style="italic", align ="left", text_font_size = "25px")

p.line(x=x, y=y)

p.add_layout(title_test, "above")


my_label = Div(text="""Your <a href="https://en.wikipedia.org/wiki/HTML">HTML</a>-supported text is 
initialized with the <b>text</b> argument.  The remaining div arguments are <b>width</b> and <b>height</b>. For
this example, those values are <i>200</i> and <i>100</i>, respectively.""",
width=200, height=100)


my_label.visible = False


callback = CustomJS(args=dict(my_label=my_label), code="""

    // if it's visible now, make it Invisible...

    if (my_label.visible == true) 

      my_label.visible = false;

    // otherwise, make it visible.

    else 

      my_label.visible = true;


""")


button = Button(label="info")

button.js_on_click(callback)

show(row(column(button, width=50), p, my_label))
```
![Screen Shot 2021-06-29 at 3 32 22 PM](https://user-images.githubusercontent.com/7526622/123856555-3c934580-d8ef-11eb-9f14-11919b924e5b.png)
user: ncclementi
issue_11373: state: open
title: [FEATURE] DataRange1d asymmetric padding
body: **Is your feature request related to a problem? Please describe.**
Currently, the "padding" parameter of a DataRange1d adds padding symmetrically on both ends of the range.

In certain applications it might be desirable to have different padding for start and end of a DataRange1d.

As example, in a timeseries stretching up to the current day, one might want to leave some padding to the right, but not to the left. 

To my knowledge, achieving this type of behavior currently requires to default to Range1d or to manually change the start and end of the DataRange1d.

**Describe the solution you'd like**
Allow the padding parameter to be specified for each side of the range.

**Describe alternatives you've considered**
Allow the padding parameter to be specified as a tuple (start_padding, end_padding), similar to bounds.
user: dnabb
issue_11372: state: open
title: [BUG] DataRange1d bounds='auto' breaks box zoom
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
Bokeh 2.3.0, Python 3.9.2, Windows 10, Firefox 89.0.1

#### Description of expected behavior and the observed behavior
Using 'auto' bounds in DataRange1d (apparently only on the y axis) in combination with a callback changing CDSViews causes the box zoom to zoom the wrong region / flip the range.

See attached gif.

#### Complete, minimal, self-contained example code that reproduces the issue

```
from bokeh.plotting import figure, output_notebook, show
from bokeh.models import ColumnDataSource, Select, CDSView, GroupFilter
from bokeh.layouts import row
from bokeh.io import curdoc

# Bokeh settings
output_notebook()

data = {'Category': ['A', 'A', 'A','B', 'B', 'B'],
        'x': [1, 2, 3, 1, 2, 3],
        'y': [1, 2, 3, 12, 20, 22]
       }

src = ColumnDataSource(data=data)

TOOLS = "box_zoom,reset"
p = figure(tools=TOOLS)

r = p.circle(x='x', y='y', source=src, size=20)
opts = ['A', 'B']
s = Select(value=opts[0], title='Choose category', options=opts)

f = GroupFilter(column_name='Category', group=s.value)
src_view = CDSView(source=src, filters=[f])
r.view = src_view

#p.x_range.bounds='auto' # This seems to be fine
p.y_range.bounds='auto' # This makes mess with the box zoom

l =  row(s,p)

def app(doc):
    def update_view(attr, old, new):
        # Updating the filter doesn't work because of https://github.com/bokeh/bokeh/issues/7273
        # I have to update the views instead
        new_filter = GroupFilter(column_name='Category', group=new)
        src_view.filters[0] = new_filter
    
    s.on_change('value', update_view)
    
    doc.add_root(l)
show(app)

```

#### Screenshots or screencasts of the bug in action


![210623-145029](https://user-images.githubusercontent.com/13344050/123099389-6ef5fc00-d432-11eb-803a-a8173e79168c.gif)

*Note: Everyone interacting in the Bokeh project's codebases, issue trackers, and discussion forums is expected to follow the [Code of Conduct](https://github.com/bokeh/bokeh/blob/master/CODE_OF_CONDUCT.md).*

user: dnabb
labels: tag: tools: Related to the toolbar and individual tools
issue_11366: state: open
title: [BUG]: Miss leading error for export png when incompartible version of selenium is installed.
body: # History
We are using the export png function of bokeh in one of our projects. This requires selenium and firefox + geckodriver
or chromium the use of phantomjs having dropped. Having successfully installed firefox and geckodriver. I was still getting error.
`"Neither firefox and geckodriver nor a variant of chromium browser and chromedriver are available on system PATH. You can install the former with 'conda install -c conda-forge firefox geckodriver'.`

I was using selenium version 3.7.0.

When I dug in it turned out [this function](https://github.com/bokeh/bokeh/blob/4c7165631d9b689f0aa66a37e32050e9f114f481/bokeh/io/webdriver.py#L108). was raising options is not a valid argument for __init__  in this line [here](https://github.com/bokeh/bokeh/blob/4c7165631d9b689f0aa66a37e32050e9f114f481/bokeh/io/webdriver.py#L83). Because the selenium version being used did not expect an options in the firefox webdriver. But this is being eaten by the general exceptions
[catch here](https://github.com/bokeh/bokeh/blob/4c7165631d9b689f0aa66a37e32050e9f114f481/bokeh/io/webdriver.py#L111).
As a result, I spent three days chasing down variant ways of installing firefox and geckodriver. 

I had wanted to say, bokeh 2.3.2 should specify selenium as a dependency and specify compatible versions of selenium as a solution in setup.py. But then I remembered that strictly Bokeh does not require selenium. The better solution then I imagine is not to mask any error raised by/in [this function](https://github.com/bokeh/bokeh/blob/4c7165631d9b689f0aa66a37e32050e9f114f481/bokeh/io/webdriver.py#L56) except those that are caused by a missing driver.

# Summary
In summary, I would say, change the function
`
def _try_create_firefox_webdriver() ->; Optional[WebDriver]:    
       try:        
             return create_firefox_webdriver()    
       except Exception:       
               return None`
 #TO
`def _try_create_firefox_webdriver() ->; Optional[WebDriver]:    
       try:        
               return create_firefox_webdriver()    
       except RunTimeError:       
               return None
`
At least this way, we can get to identify the true culprit when running the code and not have to waste time.

Alternatively in addition, I would suggest I have seen instances of `pip install package[dependant_package]==version` to be in 
documentation.

# To reproduce:
bokeh==2.3.2
selenium==3.7.0
install firefox + geckodriver
check that both are installed.
`from shutil import which
firefox = which("firefox")
geckodriver = which("geckodriver")`
Once this is done, then we are sure the error should never be `**Neither firefox + geckodriver ......**`
`from bokeh.io.webdriver import  _try_create_firefox_webdriver, create_firefox_webdriver
result = _try_create_firefox_webdriver()
print(result) # will print None, this function is masking an exception.
result = create_firefox_webdriver()  # will raise exception

**Desireable Behaviour**

`result = _try_create_firefox_webdriver()  # should bubble up the exception raised by create_firefox_webdriver except RunTimeError.`
`


user: darahsten
issue_11364: state: open
title: Docs for science-focused exporting
body: To make Bokeh a more useful tool for academic work aimed at creating plots for academic journals and other publications, it would be helpful to have additional documentation focusing on those specific applications. The goal wold be to make accessible in one place all of Bokeh’s existing functionalities relevant to publication-quality exports.
user: tcmetzger
issue_11333: state: open
title: Documentation build needs to show all errors
body: For example in https://github.com/bokeh/bokeh/pull/11330/checks?check_run_id=2809746117 it stops at 4%, no matter how many other issues there are (in this case I know there are other unrelated issues).

All errors need to be reported. I'm marking this as a feature and not discussion, pressumming that this isn't an inherent problem with sphinx.

/cc @tcmetzger 
user: mattpap
issue_11324: state: open
title: [BUG] Shift-tap multi select does not work in JupyterLab
body: #### Description of expected behavior and the observed behavior
- When one tries to use shift-tap to select multiple points in a plot in JupyterLab, the shift-tap does not work.
- The same code works in classic Jupyter notebook.
- I tried to monitor websocket messages in chrome developer tool, shift-tap does not seem to generate any message, as if the shift-tap is not handled at js side.

- The above report applied to bokeh 2.3.2. When using bokeh 2.2.3 instead, shift-tap works very intermittently. 

#### Complete, minimal, self-contained example code that reproduces the issue

```
# based on https://stackoverflow.com/a/61601164

import bokeh
from bokeh.io import show, output_notebook
from bokeh import plotting as bplt
from bokeh import layouts as blayouts
from bokeh import models as bmodels
from bokeh import io as bio

def test_tap_select(notebook_url="localhost:8888"):
    def create_interact_ui(doc):
        fig = bplt.figure(plot_width=300, plot_height=300, tools="tap")

        source = bmodels.ColumnDataSource(dict(x=[0, .2, .4, .6, .8, 1], y=[0, .1, .2, .6, .9, 1]))

        r = fig.circle('x', 'y', source=source, size=10, color='#000000',
                # set visual properties for selected glyphs
                            selection_color="#2bff00",

                            # set visual properties for non-selected glyphs
                            nonselection_fill_alpha=1.0,
                            nonselection_fill_color="#000000")

        def handler(attr, old, new):
            print('attr: {} old: {} new: {}'.format(attr, old, new))

        # r.data_source.on_change('selected', handler)
        r.data_source.selected.on_change('indices', handler)

        doc.add_root(fig)

    output_notebook(verbose=False, hide_banner=True)
    show(create_interact_ui, notebook_url=notebook_url)

print("bokeh ", bokeh.__version__)
test_tap_select(notebook_url="localhost:8889");
```

#### Stack traceback and/or browser JavaScript console output
None, other than the lack of websocket messages noted above.

#### Screenshots or screencasts of the bug in action

The initial tap of the upper right circle worked. Subsequent shift-tap on other circles all failed.

![basic_shift_click_not_working_in_jupyterlab](https://user-images.githubusercontent.com/250644/121072412-0b0eda80-c786-11eb-8e59-3b7e50505471.gif)


#### Version
bokeh: 2.3.2 (also tested on bokeh 2.2.3, shift-tap worked very intermittently with 2.2.3)
JupyterLab: 3.0.16
Browser: Chrome 91 (also tested on Firefox 89.0 with the same result)
Platform: Windows 
The issue was first reported in actual usage (rather than isolated codes here) on Mac OS X.

user: orionlee
issue_11318: state: open
title: `test_version` fails if git repository status changes during tests
body: For example if I `git add` during tests, I get this:
```py
tests/unit/bokeh/util/test_version.py F.........                                                                                      [100%]

================================================================= FAILURES ==================================================================
________________________________________________________ Test___version__.test_basic ________________________________________________________

self = <test_version.Test___version__ object at 0x7f00a3b4d370>

    def test_basic(self) -> None:
        assert isinstance(buv.__version__, str)
>       assert buv.__version__ == get_versions()['version']
E       AssertionError: assert '2.3.0-141-geaf5907d4' == '2.3.0-141-geaf5907d4-dirty'
E         - 2.3.0-141-geaf5907d4-dirty
E         ?                     ------
E         + 2.3.0-141-geaf5907d4

tests/unit/bokeh/util/test_version.py:45: AssertionError
```
This may seem like a minor thing, but it's annoying to have tests technically fail all the time, for such a irrelevant reason. Do we have to test the git-dependent part of version string in `test_version:test_basic()`?
user: mattpap
issue_11316: state: open
title: [FEATURE] Ability to dynamically change AjaxDataSource polling interval
body: Currently the polling_interval is only used as an initial configuration and can not be dynamically changed.

The following is a discourse link where I have asked the question whether this is a supported feature.
https://discourse.bokeh.org/t/dynamically-changing-bokeh-ajaxdatasource-polling-interval/7737
user: bahrikutlu
issue_11303: state: open
title: [BUG] ColorMapper does not update state in browser properly under certain circumstances
body: [Bokeh 2.3.2] [python 3.9][Chrome Version 90.0.4430.212][Windows 10.0.19041 Build 19041]

Under certain circumstances (explained in detail below) the ColorMapper does not properly propagate the model-changes to the frontend. They then are delayed by one update-step, e.g, on 1. update show the initial state, on 2. update show the state from update 1.

This bug is mentioned in a discourse thread:  [bokeh discourse thread](https://discourse.bokeh.org/t/log-colorbar-min-max-update-not-triggered/7752/10)

How to reproduce with example below:

1. **click on "greys" before you click on anything else**
2. **hit "change min and max SERVER"** -> nothing happens
3. **hit "change min and max SERVER" again** -> the selected (right) colorbar changes its low & high visually (however that's the old state that wasn't displayed before, not the new one)
4. **click on "turbo"** -> the left colorbar weirdly changes its values to some other values (it is displaying the correct, new data, actually)
5. **click on "greys" again** -> the right colorbar finally changes its values to the state of the server-model


```
    from bokeh.io import curdoc
    from bokeh.layouts import row, column
    from bokeh.models import LinearColorMapper, ColorBar, ColumnDataSource, RadioButtonGroup, Button, CustomJS
    import numpy as np
    from bokeh.palettes import Turbo256, Greys256
    from bokeh.plotting import figure

    dataset = np.random.randint(100, size=(200, 200))
    source = ColumnDataSource(data=dict(image=[dataset]))
    _min = dataset.min()
    _max = dataset.max()

    cm1 = LinearColorMapper(palette=Turbo256, low=_min, high=_max)
    cm2 = LinearColorMapper(palette=Greys256, low=_min, high=_max)

    cbar1 = ColorBar(color_mapper=cm1)
    cbar2 = ColorBar(color_mapper=cm2)

    cm_cbar_dict = {"turbo": [cm1, cbar1], "greys": [cm2, cbar2]}

    fig = figure(width=500, height=500)
    img = fig.image(source=source, dw=200, dh=200, x=0, y=0, color_mapper=cm1)

    fig.add_layout(cbar1, "right")
    fig.add_layout(cbar2, "right")

    btn = Button(label="change min and max SERVER")
    btn_js = Button(label="change min and max JS")
    radio = RadioButtonGroup(labels=list(cm_cbar_dict.keys()), active=0)


    def change_cbar(idx):
        for index, val in enumerate(cm_cbar_dict.values()):
            if index == idx:
                #val[1].visible = True
                img.glyph.color_mapper = val[0]
            else:
                #val[1].visible = False
                pass

    radio.on_click(change_cbar)


    def change_min_max():
        low = np.random.randint(50)
        high = 100 - low
        #img.glyph.color_mapper.update(low=low, high=high)
        for val in cm_cbar_dict.values():
            val[0].update(low=low, high=high)
        #print(f"Set low to {low} and high to {high}")
    btn.on_click(lambda x: change_min_max())

    change_min_max_js = CustomJS(args=dict(cms=cm_cbar_dict),
                                 code="""
                                    var low = Math.random() * 50
                                    var high = 100 - low
                                    for (let key in cms){
                                        cms[key][0].low = low
                                        cms[key][0].high = high
                                    }
                                    console.log("set low to", low, "and high to", high)
                                 """)
    btn_js.js_on_click(change_min_max_js)


    curdoc().add_root(row(column(btn, btn_js, radio), fig))

```

Note: This problem does not occur if using the js-callbacks.
Note2: This is not limited to the ColorBar, the image glyph does not recolor, as well.
user: rynkk
issue_11300: state: open
title: conda is regularly failing in CI
body: It fails regularly in different jobs, e.g. with:
```
ConnectionResetError: [Errno 104] Connection reset by peer
```
from https://github.com/bokeh/bokeh/pull/11286/checks?check_run_id=2659598526.

Perhaps we could make it more robust, e.g. by retrying commands that failed due to networking errors.
user: mattpap
issue_11294: state: open
title: [BUG] wheel zoom struggles to return to full bounds when DataRange1d bounds are set
body: This is on bokeh 2.3.2, latest Chrome.

Code looks like this:

```python
from bokeh.models import DataRange1d
from bokeh.plotting import figure, show
x = [1, 2, 3, 4, 5]
y = [6, 7, 2, 4, 5]
p = figure(x_range=DataRange1d(bounds=(1, 5), start=1, end=5), 
           y_range=DataRange1d(bounds=(2, 7), start=2, end=7))
p.line(x, y)
show(p)
```

The intention here is that you can use the wheel zoom to zoom in to an area, but not zoom out beyond the bounds specified.

However, what happens, is that as wheel zoom out is used, the zoom out grinds to a halt, never quite able to reach the bounds specified. Only the "reset" tool fully returns the view to its initial position.

Related, but probably a feature a request: it would be great to have a shortcut for "please prevent zooming out beyond the initial view", as I suspect that is quite a common requirement.
user: cjw296
labels: tag: tools: Related to the toolbar and individual tools
issue_11291: state: open
title: [BUG] Bokeh plots get broken irretrievably if transiently have zero range
body: It's not surprising that a Bokeh plot has a problem displaying when the range is zero, but (a) it would be nice if that were handled better, and (b) because it can happen transiently, it would be nice if Bokeh would recover, but it does not. E.g. if you visit this [online Bokeh 2.3.1 app](https://pydeas.readthedocs.io/en/latest/holoviz_interactions/panel_to_holoviews.html#Range-slider-for-axis-limits) and set the range slider to 2,2 (zero range in the y axis), the plot disappears, never to come back even if the range is later expanded:
![image](https://user-images.githubusercontent.com/1695496/119075733-ec9f9580-b9b6-11eb-90eb-ae18a3cf8d64.png)

This may seem like an obscure case, but it looks to me like the same thing that happens in actual plotting depending on zoom level. E.g. if you zoom into the last plot at https://examples.pyviz.org/landsat and zoom out, you quickly end up in a similar situation: 

![image](https://user-images.githubusercontent.com/1695496/119076269-e362f880-b9b7-11eb-9045-7372fb003a8f.png)
The Bokeh error is different, but this is a different version (1.1.0), so it may or may not be something similar.
user: jbednar
issue_11285: state: open
title: [BUG] Throttling behavior in PlotView
body: I have done some experiments around synchronizing Bokeh plots between a FastAPI server and a React application. I use `Document.clear()` to remove the document when the Component is unmounted, for example when the single page app navigates away from the page with the plot.

This leads to the following error, both in Bokeh 2.3.2 and current master.

```
bokeh.js:37282 Uncaught (in promise) TypeError: Cannot read property 'layout' of undefined
    at needs_layout (bokeh.js:37282)
    at PlotView._invalidate_layout (bokeh.js:37289)
    at PlotView.repaint (bokeh.js:37391)
    at bokeh.js:37082
    at later (bokeh.js:37574)
```

I figured out this is because the view will schedule a throttled repaint, using a promise. Removing that throttling removes this exception.

I also don't think it is a wise decision to throttle repainting in this way. Better would be to have it made optional, with a throttling rate and the option of requestAnimationFrame. Though the latter may not be that useful with more complex plots. 
user: akloster
issue_11275: state: open
title: [BUG] PolyDrawTool is broken in gridplot when `merge_tools=True`
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
bokeh = 2.3.2 (also not working in 2.1.1 but working in 2.0.2)
python = 3.7.6
OS = linux-64 (Ubuntu 20.04)
browser = Chrome

#### Description of expected behavior and the observed behavior
When attaching the same PolyDrawTool to two plots inside gridplot with `merge_tools=True` it should be possible to select and use the tool by clicking with mouse on it's icon.  Also only one icon should be shown.

However, nothing happens when tool icon is clicked.  Active tool marker goes away from 'pan' tool but it does not show under PolyDrawTool and drawing does not work. Also two icons are shown for the same tool.

#### Complete, minimal, self-contained example code that reproduces the issue

```
import bokeh
from bokeh.plotting import figure, output_file, save, ColumnDataSource, gridplot
from bokeh.models import PolyDrawTool

output_file('test-bokeh-gridplot-tools.html')

p1 = figure(x_range=(0, 10), y_range=(0, 10), width=400, height=400)
p2 = figure(x_range=(0, 10), y_range=(0, 10), width=400, height=400)

line_source = ColumnDataSource({'xs': [], 'ys': [],})
line_renderer_p1 = p1.multi_line('xs', 'ys', source=line_source)
line_renderer_p2 = p2.multi_line('xs', 'ys', source=line_source)
line_tool = PolyDrawTool(renderers=[line_renderer_p1, line_renderer_p2])
p1.add_tools(line_tool)
p2.add_tools(line_tool)

# Not working
save(gridplot([[p1, p2]], merge_tools=True))

# This works
#save(gridplot([[p1, p2]], merge_tools=False))
```

#### Stack traceback and/or browser JavaScript console output
No errors/warnings are shown in python nor browser JavaScript console output

#### Screenshots or screencasts of the bug in action
This is how toolbar looks after clicking on PolyDrawTool:
![image](https://user-images.githubusercontent.com/3921196/118287312-03884880-b4dc-11eb-809e-ea7365fc07ad.png)

This is how it looks in bokeh 2.0.2 after clicking on PolyDrawTool (it still duplicates the tool though, but at least the drawing works):
![image](https://user-images.githubusercontent.com/3921196/118287883-8d381600-b4dc-11eb-8cc3-39c03b850fa6.png)


user: nasedil
labels: tag: tools: Related to the toolbar and individual tools
issue_11271: state: open
title: Make invalidation of built plots less aggressive for local docs
body: As discussed in #11269, we might be able to speed up local docs builds by making the invalidation of built plots less aggressive (see [comment ](https://github.com/bokeh/bokeh/discussions/11269?converting=1#discussioncomment-732094)by @bryevdv).
user: tcmetzger
issue_11270: state: open
title: [FEATURE] Add flags to speed uplocal docs build
body: As discussed in #11269, we would be able to speed up local docs builds by adding flags to suppress different parts of the build (see [comment ](https://github.com/bokeh/bokeh/discussions/11269?converting=1#discussioncomment-732094)by @bryevdv).
user: tcmetzger
issue_11229: state: open
title: [FEATURE] Add standard way for LoginHandler to add user information available to the Bokeh app
body: When implementing authentication for Bokeh apps there is no direct way of accessing information about the logged in user in the Bokeh apps. This information is sometimes needed to request data on behalf of the user instead of all users sharing the same account when accessing data.

The information can be retrieved through curdoc().session_context.request.cookies though it would be useful to make it available in a more explicit way.

Adding the following to a Bokeh app works to get the user given that the BOKEH_COOKIE_SECRET is set in the environment, the user cookie is name “user” and set using set_secure_cookie in the LoginHandler.

```
from tornado.web import decode_signed_value
import os

def get_user():
    user = decode_signed_value(
        os.environ.get('BOKEH_COOKIE_SECRET'),
        "user",
        curdoc().session_context.request.cookies["user"],
        max_age_days=31,
        min_version=None,
    )
    return user
```

Adding a session_context.user or similar that can be set to an object by the LoginHandler in some way would make it easy to understand where to get the user information from and also to create a Bokeh package with default implementation for standard authentication providers in a similar way as is provided by the Panel project. https://panel.holoviz.org/user_guide/Authentication.htm

user: oyse
issue_11199: state: open
title: Make draw/edit tools work with non-linear scales
body: - [ ] address test failures
- [ ] add tests for non-linear axes

fixes #11196

user: mattpap
issue_11196: state: open
title: [BUG] PointDrawTool moves points incorrectly with Log axis
body: One video is worth a thousand issue titles:

https://user-images.githubusercontent.com/11263395/115973739-f7cce600-a51c-11eb-956e-a7397e0f1ee6.mp4

Moving around points is erroneous and inconsistent as shown. Here's the code:

```
from bokeh.models import PointDrawTool
from bokeh.plotting import figure
from bokeh.io import show

fig = figure(x_range=(0, 100), y_range=(0.01, 100), y_axis_type='log')
dot = fig.scatter(x=[50], y=[50], size=20)

pdtool = PointDrawTool(add=False, renderers=[dot])
fig.add_tools(pdtool)
fig.toolbar.active_multi = pdtool

show(fig)
```

Some setup info:
python=3.7.10
bokeh=2.3.1
Tested with chromium and Firefox on up to date Manjaro.
user: Haptein
issue_11192: state: open
title: [FEATURE] Make bokeh support additional value types: astropy `Quantity` and `Time`
body: **Is your feature request related to a problem? Please describe.**
Bokeh can handle plots of numeric, and datetime values (as the data source of a plot). When the type of underlying data is slightly more complicated, such as astropy's [`Quantity`](https://docs.astropy.org/en/stable/units/quantity.html) (numeric values with units) and [`Time`](https://docs.astropy.org/en/stable/time/) (specialized time type), users need to convert them to Float when they setup bokeh data source.

As an example, in the `lc_source` (the source that backs a bokeh plot)below, when the underlying `flux` is a complex astropy `Quantity`, users need to convert the `Quantity` to simple float:
```python
    lc_source.data["flux"] = lc_new.flux.value
```

Another example is in setting bokeh widgets, e.g., range of a slider
```python
   fig_lc.y_range.end = lc_new.flux.max().value
```

It gets tedious (and error prone) when similar conversion is needed throughout the code.

**Describe the solution you'd like**

Ideally, with some one-time setup, users would not need to perform conversion every time the bokeh data source is updated:

```python
    lc_source.data["flux"] = lc_new.flux
    ...
    fig_lc.y_range.end = lc_new.flux.max()
```

I don't know enough of bokeh to suggest how to do it more specifically. I imagine some mechanism needs to be created so that custom value types can be understood by bokeh.

As a similar example, astropy can make matpotlib understand `Time` and `Quantity` by adding matplotlib plugin. See their [Quantity support](https://docs.astropy.org/en/v4.2/api/astropy.visualization.quantity_support.html) and [Time support](https://docs.astropy.org/en/v4.2/api/astropy.visualization.time_support.html) respectively.

**Describe alternatives you've considered**
Right now, we use various helpers to make the conversion less painless / error prone.

For example, for codes such as

```python
    lc_source.data["flux"] = lc_new.flux.value
```

we create a helper `update_source(src, new_data)` so that the codes would become
```python
   update_source(lc_source, {flux: lc_new.flux})
```

The `update_source` helper would internally convert `lc_new.flux` to Float.

The `update_source` helper doesn't help for the cases for setting a widget's properties though.


**Additional context**

The request is made, based on the discussion at https://github.com/lightkurve/lightkurve/issues/1033 

user: orionlee
issue_11186: state: open
title: [FEATURE] Move/drag js callbacks for PointDrawTool with bokeh serve
body: I'm looking for a way to add custom js callbacks during move/drag events with the PointDrawTool. There was a discussion about this on [Discourse](https://discourse.bokeh.org/t/change-when-pointdrawtool-syncs/7654).

The situation is that I'd like to update data on one plot, while the data on another plot is getting moved. Here is a simplified example app:
```python
# file is main.py within a "simple-pointdraw" directory.
from bokeh.models import ColumnDataSource, PointDrawTool, CustomJS
from bokeh.plotting import figure
from bokeh.io import curdoc
from bokeh.layouts import row, layout

left = figure(x_range=(0, 10), y_range=(0, 10), tools=[],
           title='Point Draw Tool')
left.background_fill_color = 'lightgrey'
right = figure(x_range=(0, 10), y_range=(0, 10), tools=[],
           title='Synced Plot')
right.background_fill_color = 'lightgrey'

source = ColumnDataSource({
    'x': [1, 5, 9],
    'y': [1, 5, 9],
    'y2': [2, 6, 10],
    'color': ['red', 'green', 'yellow'],
})

js = """
for (var i = 0; i<cb_obj.data['y'].length; i++) {
	var y = cb_obj.data['y'][i];
	cb_obj.data['y2'][i] = y + 1;
}
cb_obj.change.emit();
"""
source.js_on_change('data', CustomJS(code=js))

left_renderer = left.scatter(x='x', y='y', source=source, color='color', size=10)
right_renderer = right.scatter(x='x', y='y2', source=source, color='color', size=10)

draw_tool = PointDrawTool(renderers=[left_renderer], empty_value='black')

left.add_tools(draw_tool)
left.toolbar.active_tap = draw_tool

curdoc().add_root(row(left, right))
curdoc().title = "Synced Plots"
```
The modified y-values in the callback don't get updated until the movement is complete:
![bokeh-move-pts](https://user-images.githubusercontent.com/997120/115614157-2c813900-a2a2-11eb-98a4-91244860a21f.gif)

This example is fairly simple and could probably be achieved with [CustomJSTransform](https://docs.bokeh.org/en/latest/docs/user_guide/data.html?highlight=customjstransform#including-javascript-code-with-customjstransform), but my exact use case is more complicated and would require a [custom tool](https://docs.bokeh.org/en/latest/docs/user_guide/extensions_gallery/tool.html) probably.
user: mccarthyryanc
issue_11173: state: open
title: Allow alpha in Image to take array values [FEATURE]
body: `matplotlib` offers array-valued inputs to the alpha parameter of an image plot, enabling a linear colormap with opacity on each pixel determined by a separate array (imagine, for example, plotting a linear-valued parameter with increasing opacity for increased confidence, so that low-confidence measurements in the image are faint against the background). To me, this feature seems useful, though I don't know if it's in very high demand (or that it would be easy to implement). There are workarounds with image_rgba, but they require the user to manually compute the rgb for their colormap of choice and rescale it for their values of interest, which isn't tremendously difficult but is ugly and repetitive.

Ideally, this would be as simple as allowing the `alpha` keyword argument to accept array-like inputs or creating an `alpha_mask` separate keyword.

user: StephenThornquist
issue_11155: state: open
title: [FEATURE] Adding a flipped_fill option to varea
body: 
**Is your feature request related to a problem? Please describe.**
It's currently very complicated to fill the area between 2 lines in different colors according to the position of a line with respect to the other.

**Describe the solution you'd like**
It would be nice to add a flipped_fill option to the varea glyph that would automatically change color according to the value of y1 values comparatively to y2 ones. Two colors should be provided of course in the color field.

**Describe alternatives you've considered**
I tried to do it by using a CDSView in varea, or by using patches, to no avail.

**Additional context**
Here is a screenshot showing the expected result:
![range2](https://user-images.githubusercontent.com/17707763/114226078-69c8fb00-9973-11eb-9154-a828fb6eb013.png)

user: Mark531
issue_11136: state: open
title: [FEATURE] +/- keyboard shortcuts to zoom in and out in tile based maps
body: **Problem**
Every time I try to zoom in and out in a tile based map (like Google Maps, Mapbox, Open Street Map, Plotly, Leaflet etc) my first instinct is to use the -/+ keys, and they work uniformly everywhere. Except in Bokeh tile maps, as this is not an implemented feature here.

**Recommended solution**
Make the +/- keys (numpad and normal versions both) zoom the map in and out by a discrete amount.
I admit this is a slightly more complicated issue than it looks at first glance, as you can not highjack every keypress on the page because they could be intended for other elements. As far as I know most map implementations work in a way that tracks if the map itself is "in focus" (meaning the last mouse click happened inside the map and/or the mouse pointer is hovering the map) and if it is, then they highjack a few specific keypresses, +/- among them. This shouldn't be a big issue as the mouse wheel zoom already works this way.

**Benefits of having this feature**
 - It is an expected functionality of a tile based map. Every one of the examples mentioned above include this, so users would expect this to work and then be frustrated of it isn't. 
 - It is actually a very good way to zoom:
     - It works in discrete steps, so no need to try to guess the necessary intensity of the mouse zoom for the intended zoom levels and then still overshooting and them try to zoom back a bit etc.
     - It is as quick as you need it to be, you just have to press the same key multiple times in rapid succession and you're there. 
     - Yes, zooming with the Box Zoom can be fairly quick, but then you can not use the pan tool at the same time. With keyboard zoom, you would be able to pan with mouse and zoom with keyboard at the same time.

user: ka-steve
issue_11131: state: open
title: [BUG] HoverTool does not update tooltip on data_source change
body: The HoverTool only updates on mouse-movement, not on data-change.

Now, to begin, I'd like to add, that this could also be seen as a feature-request.
HOWEVER, as this is not what an end user would expect, at all, I'm more inclined to call it a bug.
Feel free to move this to feature-request if there is an agreement that this is not detrimental as it stands.

This bug/feature strikes me as absolutely essential for interactive plots, where users can use the HoverTool to examine an interactive, automatically changing plot (without having to do anything, e.g., mouse movement). 
Having the hover just being stuck at a certain, outdated state instantly decreases the quality and feel of the software, making it feel unpolished.

I would expect either
1. e.g., an optional parameter for the HoverTool, that makes the HoverTool listen to changes of the underlying renderer.data_source (for either all or just the hovered renderer).
2. a way to manually force an update on a HoverTool, like a `refresh`, `force_update`, etc.
3. a communicated work-around for this problem

I am using:
bokeh 2.3, python 3.9, Google Chrome 89.0.4389.114

To reproduce simply start the animation of the Gapminder demo, hover over any country-bubble and stop moving your mouse. The country name will be shown in the hovertooltip even though the country-bubble is already not hovered, anymore.

This example code is just taken from the demo gallery (Gapminder)

```
import numpy as np
import pandas as pd

from bokeh.io import curdoc
from bokeh.layouts import layout
from bokeh.models import (Button, CategoricalColorMapper, ColumnDataSource,
                          HoverTool, Label, SingleIntervalTicker, Slider, )
from bokeh.palettes import Spectral6
from bokeh.plotting import figure


def process_data():
    from bokeh.sampledata.gapminder import fertility, life_expectancy, population, regions

    # Make the column names ints not strings for handling
    columns = list(fertility.columns)
    years = list(range(int(columns[0]), int(columns[-1])))
    rename_dict = dict(zip(columns, years))

    fertility = fertility.rename(columns=rename_dict)
    life_expectancy = life_expectancy.rename(columns=rename_dict)
    population = population.rename(columns=rename_dict)
    regions = regions.rename(columns=rename_dict)

    regions_list = list(regions.Group.unique())

    # Turn population into bubble sizes. Use min_size and factor to tweak.
    scale_factor = 200
    population_size = np.sqrt(population / np.pi) / scale_factor
    min_size = 3
    population_size = population_size.where(population_size >= min_size).fillna(min_size)

    return fertility, life_expectancy, population_size, regions, years, regions_list

fertility_df, life_expectancy_df, population_df_size, regions_df, years, regions_list = process_data()

df = pd.concat({'fertility': fertility_df,
                'life': life_expectancy_df,
                'population': population_df_size},
               axis=1)

data = {}

regions_df.rename({'Group': 'region'}, axis='columns', inplace=True)
for year in years:
    df_year = df.iloc[:, df.columns.get_level_values(1) == year]
    df_year.columns = df_year.columns.droplevel(1)
    data[year] = df_year.join(regions_df.region).reset_index().to_dict('series')

source = ColumnDataSource(data=data[years[0]])

plot = figure(x_range=(1, 9), y_range=(20, 100), title='Gapminder Data', plot_height=300)
plot.xaxis.ticker = SingleIntervalTicker(interval=1)
plot.xaxis.axis_label = "Children per woman (total fertility)"
plot.yaxis.ticker = SingleIntervalTicker(interval=20)
plot.yaxis.axis_label = "Life expectancy at birth (years)"

label = Label(x=1.1, y=18, text=str(years[0]), text_font_size='93px', text_color='#eeeeee')
plot.add_layout(label)

color_mapper = CategoricalColorMapper(palette=Spectral6, factors=regions_list)
plot.circle(
    x='fertility',
    y='life',
    size='population',
    source=source,
    fill_color={'field': 'region', 'transform': color_mapper},
    fill_alpha=0.8,
    line_color='#7c7e71',
    line_width=0.5,
    line_alpha=0.5,
    legend_group='region',
)
plot.add_tools(HoverTool(tooltips="@Country", show_arrow=False, point_policy='follow_mouse'))


def animate_update():
    year = slider.value + 1
    if year > years[-1]:
        year = years[0]
    slider.value = year


def slider_update(attrname, old, new):
    year = slider.value
    label.text = str(year)
    source.data = data[year]


slider = Slider(start=years[0], end=years[-1], value=years[0], step=1, title="Year")
slider.on_change('value', slider_update)

callback_id = None


def animate():
    global callback_id
    if button.label == '► Play':
        button.label = '❚❚ Pause'
        callback_id = curdoc().add_periodic_callback(animate_update, 200)
    else:
        button.label = '► Play'
        curdoc().remove_periodic_callback(callback_id)


button = Button(label='► Play', width=60)
button.on_click(animate)

layout = layout([
    [plot],
    [slider, button],
], sizing_mode='scale_width')

curdoc().add_root(layout)
curdoc().title = "Gapminder"

```

Here's a video: [Imgur](https://i.imgur.com/fUQo5UM.mp4)

I mentioned this already in the [bokeh community discourse:](https://discourse.bokeh.org/t/how-to-force-an-update-of-the-hovertool/7559/4)

user: rynkk
issue_11124: state: open
title: [FEATURE] Add Selection support to images
body: E.g., medical images are low-resolution and often need statistical operations performed on certain regions.
Sadly bokeh does not support selection for images at all.

Native support for (at least limited) selection would be useful. Lasso-selection would be expensive for large selections but a rectangular-selection would maybe be doable if there's some optimization done.

Currently the workaround would be to use SelectionGeometry-events and manually work with the bounds and indices of the image.

[Link to Bokeh discource](https://discourse.bokeh.org/t/correct-way-to-use-selection-lasso-box-on-an-image/7552/3)
user: rynkk
issue_11114: state: open
title: [BUG] CDS js_on_change callback not firing, python callback is firing
body: 
#### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
Bokeh 2.3.0, chrome browser, python 3.6

#### Description of expected behavior and the observed behavior
I'm attempting to trigger a js_on_change callback from a cds that changes in a button-related callback, however it doesn't trigger, I see no updates in the browser console. An on_change python callback however does manage to trigger from the same cds. I initially mentioned it on discourse here.. https://discourse.bokeh.org/t/cds-js-on-change-callback-not-firing-on-bokeh-server-but-python-callback-does-fire/7535. I believe it should trigger as I'm completely replacing the cds.data rather than changing it in-place which I understand is a factor it it triggering.

edit: possibly related to this bug; https://github.com/bokeh/bokeh/issues/9613

Thanks for any input, suggestions for work-arounds, or even bug-fixes if it's an actual bug.

Complete, minimal, self-contained example code that reproduces the issue below (running in bokeh server)

```
from bokeh.models import ColumnDataSource
from bokeh.io import curdoc
from bokeh.layouts import layout
from bokeh.models import Button
from bokeh import events
from bokeh.models.callbacks import CustomJS

def bkapp(doc):
    ### set up dummy cds and a button
    cds_counter=ColumnDataSource(data={"counter":[0]})
    button_dummy=Button(label="Test Button_dummy", button_type="success", width=60)

    ### callback for the button, to update the cds
    global dummy_int
    dummy_int=0
    def button_callback(event):
        print("button has been pressed")
        global dummy_int
        dummy_int +=1
        temp=dummy_int
        cds_counter.data={"counter": [temp]}
        print(cds_counter.data["counter"])
    button_dummy.on_click(button_callback)

    ### callbacks I'd like to trigger after pressing the button, via the cds change
    js_counter_callback = CustomJS(args=dict(cds_counter=cds_counter), code="""
                console.log("test test test is not running")
                console.log(cds_counter.data["counter"])
                """)
    def py_counter_callback(attr, old, new):
        print("should run when counter in cds changes??")
    cds_counter.on_change("data", py_counter_callback)
    cds_counter.js_on_change("data", js_counter_callback)

    doc.add_root(layout(button_dummy))
global doc
doc = curdoc()
bkapp(doc)

```



user: sirspoon
issue_11104: state: open
title: [FEATURE] Option for DataTable column sorting to place NaNs last
body: When sorting a TableColumn with NumberFormatter formatting, NaN values are treated like large numbers: rising to the top when the column is in descending order, and moving to the bottom when in ascending order. This remains the case when you set a value for `nan_format`. It would be nice to have the option to force these to display last in either sorting direction, mimicking the pandas Series sorting behavior with missing data, and MATLAB's MissingPlacement = 'last' sorting option.

(As a workaround in the meantime I've been replacing nans with low/negative numbers and hiding them with CSS + HTMLTemplateFormatter.)

Example creating a table with NaNs (tested in bokeh 2.3.0):
```python
import numpy as np
from bokeh import io as bkio
import bokeh.models as bkm

source = bkm.ColumnDataSource({
    'journal': ['The Lancet', 'Clinical and experimental pediatrics', 
                'Anales de Pediatria', 'Annals of translational medicine', 'CMAJ'],
    'citescore': [10.28, np.nan, 0.43, np.nan, 1.12],
    })
d = bkm.DataTable(columns=[bkm.widgets.TableColumn(field='journal', title='journal'),
                           bkm.widgets.TableColumn(field='citescore', title='citescore', 
                                                   formatter=bkm.NumberFormatter(format='0.0'))], 
                  source=source)
bkio.show(d)
```

NaNs are sorted to the top in descending order:
<img width="611" alt="image" src="https://user-images.githubusercontent.com/5185412/112624371-7a328f00-8e25-11eb-8a65-359d372c8100.png">

user: sggaffney
issue_11102: state: open
title: [FEATURE] Add support for "shared" coordinates for ColumnDataSource with MultiLine
body: **Is your feature request related to a problem? Please describe.**

Let's say I want to make a plot with multiple lines using a `ColumnDataSource`, and they all have the same X coordinates; this can be accomplished by making multiple `Line` plots on the same figure, with each one having their own column specifying the Y coordinates:

```python
p = figure(plot_width=400, plot_height=400)

source = ColumnDataSource({
    "x": [1, 2, 3],
    "y1": [4, 5, 6],
    "y2": [7, 8, 9]
})

p.line(x="x", y="y1", line_color="firebrick", source=source)
p.line(x="x", y="y2", line_color="navy", source=source)
```

In this case, the `"x"` column is effectively "shared" by all of the lines. However, if we want to create the same plot using `MultiLine`, we must instead specify X coordinates for each line, even if they are identical:

```python
p = figure(plot_width=400, plot_height=400)

source = ColumnDataSource({
    "xs": [[1, 2, 3], [1, 2, 3]],
    "ys": [[4, 5, 6], [7, 8, 9]],
    "colors": ["firebrick", "navy"],
})

p.multi_line(xs="xs", ys="ys", line_color="colors", source=source)
```

In a sense, these extra X coordinates are redundant.

**Describe the solution you'd like**

Some way to implicitly or explicitly mark coordinates as "shared" in a `ColumnDataSource` used for a `MultiLine` plot:

```python
implicit = ColumnDataSource({
    "xs": [1, 2, 3],  # this will always be used as X coords
    "ys": [[4, 5, 6], [7, 8, 9]],
    "colors": ["firebrick", "navy"],
})

explicit = ColumnDataSource({
    "xs": {
        "coords": [[1, 2, 3]],
        "mapper": lambda idx: 0  # function mapping Y coords index to shared X coords index
    }
    "ys": [[4, 5, 6], [7, 8, 9]],
    "colors": ["firebrick", "navy"],
})
```
 
**Describe alternatives you've considered**

Coordinates can be "shared" relatively easily by making a plot with several `Lines` instead of `MultiLine`. However, in cases with a large number of lines, it can be performant to use a `MultiLine` instead.

user: charlesbluca
issue_11101: state: open
title: [FEATURE] Add support for "coordinate" data streaming for ColumnDataSource with MultiLine
body: **Is your feature request related to a problem? Please describe.**

Let's say I want to make a `MultiLine` plot using a `ColumnDataSource`, from which new data is streamed in:

```python
p = figure(plot_width=400, plot_height=400)

source = ColumnDataSource({
    "xs": [[1, 2, 3], [1, 2, 3]],
    "ys": [[4, 5, 6], [7, 8, 9]],
    "colors": ["firebrick", "navy"],
})

p.multi_line(xs="xs", ys="ys", line_color="colors", source=source)
```

I have 2 lines and would like to stream additional coordinates to them. However, `ColumnDataSource.stream()` only allows data to be appended directly to a column's values. This means that new coordinates cannot be added to preexisting lines; only new lines can be added to the overall plot:

```python
>>> source.stream({"xs": [[4], [4]], "ys": [[7], [10]]})
ValueError: Must stream updates to all existing columns (missing: colors)

>>> source.stream({"xs": [[1, 2, 3]], "ys": [[10, 11, 12]], "colors": ["firebrick"]})

>>> source.data
{'xs': [[1, 2, 3], [1, 2, 3], [1, 2, 3]],
 'ys': [[4, 5, 6], [7, 8, 9], [10, 11, 12]],
 'colors': ['firebrick', 'navy', 'firebrick']}
```

Without this functionality, I am unable to stream new coordinates to my original `MultiLine` plot through a `ColumnDataSource`.

**Describe the solution you'd like**

A separate streaming function to stream new coordinates to preexisting lines, effectively making the above attempt work:

```python
>>> source.stream({"xs": [[4], [4]], "ys": [[7], [10]]})

>>> source.data
{'xs': [[1, 2, 3, 4], [1, 2, 3, 4]],
 'ys': [[4, 5, 6, 7], [7, 8, 9, 10]],
 'colors': ['firebrick', 'navy']}
```

**Describe alternatives you've considered**

I've tried to manually append the coordinates to the desired lines in the `ColumnDataSource`, but that doesn't result in updates on the plot.

Another way to achieve a multi line plot with data streaming is to use several `Line` plots on the same figure, all using their own columns in the `ColumnDataSource`:

```python
p = figure(plot_width=400, plot_height=400)

source = ColumnDataSource({
    "x": [1, 2, 3],
    "y1": [4, 5, 6],
    "y2": [7, 8, 9]
})

p.line(x="x", y="y1", line_color="firebrick", source=source)
p.line(x="x", y="y2", line_color="navy", source=source)
```

This is a pretty good alternative for smaller, simpler plots. However, this can get slow in use cases with more (30+) lines, and prevents the use of columns for metadata to be used in a `HoverTool`.

**Additional context**

I ran into this problem trying to make multi-line time series in the Dask Distributed dashboard (dask/distributed#4627); I ended up going with the alternative strategy stated above in that case, and this feature would enable the use of `HoverTool` in that scenario.
user: charlesbluca
issue_11082: state: open
title: Auto-ranging for wedges (and single circles?)
body: In answering #11081 I recalled that `wedge` gyphs do not yet participate in auto-ranging. It's probably past time that we make that work. For wedge-like glyphs it might be useful to offer a flag to configure whether the bounds should be "tight" to just the actual glyph, or "loose" which would return bounds for the full circle that the wedge is part of. 

Also in answering #11081 I had to use an invisible `rect` as a WAR, because a circle with data `radius` was not auto-ranged. Is this a regression? I thought circles used to auto-range even with `radius`. cc @mattpap 

Edit: I suppose the issue with circles is really only limited to when there is "one, big" circle. Auto-ranging of lots of "point" circle markers [still functions as expected](http://docs.bokeh.org/en/latest/docs/gallery/color_scatter.html). 
user: bryevdv
issue_11075: state: open
title:  TapTool(behavior='inspect', callback=OpenURL()) doesn't work due to empty selection #6580 [BUG]
body: This is #6580 but with a proper reproduction.

I would like to open a URL when the user clicks a point in a scatterplot. TapTool(behavior='inspect', callback=OpenURL()) seems perfect for this behavior. Using the following code, I would expect to obtain a plot that opens a URL when a point is clicked. However, the plot does nothing when a point is clicked.

```
from bokeh.models import ColumnDataSource, OpenURL, TapTool
from bokeh.plotting import figure, output_file, show

output_file("openurl.html")

p = figure(plot_width=400, plot_height=400,
           tools="tap", title="Click the Dots")

source = ColumnDataSource(data=dict(
    x=[1, 2, 3, 4, 5],
    y=[2, 5, 8, 2, 7],
    color=["navy", "orange", "olive", "firebrick", "gold"]
    ))

p.circle('x', 'y', color='color', size=20, source=source)

url = "http://www.colors.commutercreative.com/@color/"
taptool = p.select(type=TapTool)
taptool.behavior = 'inspect' # 'inspect', 'select'
taptool.callback = OpenURL(url=url)

show(p)
```

As a workaround, I am able to use taptool with behavior 'select', which successfully opens a URL when a point is clicked, as featured in the documentation example I copy/pasted/modified into the above code block. Unfortunately, behavior='select'  has the undesired side-effect of setting the selection on the plot:

```
from bokeh.models import ColumnDataSource, OpenURL, TapTool
from bokeh.plotting import figure, output_file, show

output_file("openurl.html")

p = figure(plot_width=400, plot_height=400,
           tools="tap", title="Click the Dots")

source = ColumnDataSource(data=dict(
    x=[1, 2, 3, 4, 5],
    y=[2, 5, 8, 2, 7],
    color=["navy", "orange", "olive", "firebrick", "gold"]
    ))

p.circle('x', 'y', color='color', size=20, source=source)

url = "http://www.colors.commutercreative.com/@color/"
taptool = p.select(type=TapTool)
taptool.behavior = 'select' # 'inspect', 'select'
taptool.callback = OpenURL(url=url)

show(p)
```

I suspect the fix involves updating the CustomJS behind OpenURL to separately invoke hit testing code rather than simply looking at the selection.

bokeh.__version__ = '2.2.3'

Thanks!
user: jjoonathan
issue_11074: state: open
title: Bokeh server is too verbose in development mode
body: For example:
```
$ BOKEH_DEV=true BOKEH_RESOURCES=server-dev bokeh serve --port=5007 examples/bugs/issue11033.py 
2021-03-17 13:33:03,665 Starting Bokeh server version 2.3.0-21-g79d8b9d69 (running on Tornado 6.1)
2021-03-17 13:33:03,667 User authentication hooks NOT provided (default user enabled)
2021-03-17 13:33:03,667 These host origins can connect to the websocket: ['localhost:5007']
2021-03-17 13:33:03,667 Patterns are:
2021-03-17 13:33:03,668   [('/issue11033/?',
2021-03-17 13:33:03,668     <class 'bokeh.server.views.doc_handler.DocHandler'>,
2021-03-17 13:33:03,669     {'application_context': <bokeh.server.contexts.ApplicationContext object at 0x7fca535a7d30>,
2021-03-17 13:33:03,669      'bokeh_websocket_path': '/issue11033/ws'}),
2021-03-17 13:33:03,669    ('/issue11033/ws',
2021-03-17 13:33:03,669     <class 'bokeh.server.views.ws.WSHandler'>,
2021-03-17 13:33:03,670     {'application_context': <bokeh.server.contexts.ApplicationContext object at 0x7fca535a7d30>,
2021-03-17 13:33:03,670      'bokeh_websocket_path': '/issue11033/ws',
2021-03-17 13:33:03,670      'compression_level': None,
2021-03-17 13:33:03,670      'mem_level': None}),
2021-03-17 13:33:03,670    ('/issue11033/metadata',
2021-03-17 13:33:03,671     <class 'bokeh.server.views.metadata_handler.MetadataHandler'>,
2021-03-17 13:33:03,671     {'application_context': <bokeh.server.contexts.ApplicationContext object at 0x7fca535a7d30>,
2021-03-17 13:33:03,671      'bokeh_websocket_path': '/issue11033/ws'}),
2021-03-17 13:33:03,671    ('/issue11033/autoload.js',
2021-03-17 13:33:03,671     <class 'bokeh.server.views.autoload_js_handler.AutoloadJsHandler'>,
2021-03-17 13:33:03,672     {'application_context': <bokeh.server.contexts.ApplicationContext object at 0x7fca535a7d30>,
2021-03-17 13:33:03,672      'bokeh_websocket_path': '/issue11033/ws'}),
2021-03-17 13:33:03,672    ('/?',
2021-03-17 13:33:03,672     <class 'bokeh.server.views.root_handler.RootHandler'>,
2021-03-17 13:33:03,672     {'applications': {'/issue11033': <bokeh.server.contexts.ApplicationContext object at 0x7fca535a7d30>},
2021-03-17 13:33:03,673      'index': None,
2021-03-17 13:33:03,673      'prefix': '',
2021-03-17 13:33:03,673      'use_redirect': True}),
2021-03-17 13:33:03,673    ('/static/extensions/(.*)',
2021-03-17 13:33:03,674     <class 'bokeh.server.views.multi_root_static_handler.MultiRootStaticHandler'>,
2021-03-17 13:33:03,674     {'root': {}}),
2021-03-17 13:33:03,674    ('/static/(.*)',
2021-03-17 13:33:03,674     <class 'bokeh.server.views.static_handler.StaticHandler'>)]
2021-03-17 13:33:03,677 Bokeh app running at: http://localhost:5007/issue11033
2021-03-17 13:33:03,677 Starting Bokeh server with process id: 602392
2021-03-17 13:33:10,336 Subprotocol header received
2021-03-17 13:33:10,337 WebSocket connection opened
2021-03-17 13:33:10,338 Receiver created for Protocol()
2021-03-17 13:33:10,339 ProtocolHandler created for Protocol()
2021-03-17 13:33:10,339 ServerConnection created
2021-03-17 13:33:10,343 Sending pull-doc-reply from session '6yz5iOHA0V8VEC4GjDzbVId62yhlkVwQK38mRkAS5aza'
2021-03-17 13:33:11,992 WebSocket connection closed: code=1001, reason=None
2021-03-17 13:33:12,414 Subprotocol header received
2021-03-17 13:33:12,415 WebSocket connection opened
2021-03-17 13:33:12,423 Receiver created for Protocol()
2021-03-17 13:33:12,423 ProtocolHandler created for Protocol()
2021-03-17 13:33:12,423 ServerConnection created
2021-03-17 13:33:12,441 Sending pull-doc-reply from session 'kxy050tObd8lW4A7lHxM502Rhcu3QFHJqFTOz6lJPB7r'
2021-03-17 13:33:18,684 [pid 602392] 1 clients connected
2021-03-17 13:33:18,684 [pid 602392]   /issue11033 has 2 sessions with 1 unused
2021-03-17 13:33:33,689 [pid 602392] 1 clients connected
2021-03-17 13:33:33,690 [pid 602392]   /issue11033 has 2 sessions with 1 unused
2021-03-17 13:33:37,684 Scheduling 1 sessions to discard
2021-03-17 13:33:37,684 Discarding session '6yz5iOHA0V8VEC4GjDzbVId62yhlkVwQK38mRkAS5aza' last in use 25691.255631029606 milliseconds ago
2021-03-17 13:33:37,686 Deleting 1 modules for <bokeh.document.document.Document object at 0x7fca535963a0>
2021-03-17 13:33:48,687 [pid 602392] 1 clients connected
2021-03-17 13:33:48,687 [pid 602392]   /issue11033 has 1 sessions with 0 unused
2021-03-17 13:34:03,687 [pid 602392] 1 clients connected
2021-03-17 13:34:03,687 [pid 602392]   /issue11033 has 1 sessions with 0 unused
2021-03-17 13:34:18,680 [pid 602392] 1 clients connected
2021-03-17 13:34:18,680 [pid 602392]   /issue11033 has 1 sessions with 0 unused
2021-03-17 13:34:33,683 [pid 602392] 1 clients connected
2021-03-17 13:34:33,683 [pid 602392]   /issue11033 has 1 sessions with 0 unused
```
And this goes on.
user: mattpap
issue_11056: state: open
title: Allow a model to be used only once in the given scope
body: Hi All,

I've encountered a problem in bokeh 2.3.0 when double adding renderers in a bokeh serve app, more specifically the web socket closes and reopens making the app unusable whenever a column data source updating the plot shrinks in column length relative to the last update, as long as it keeps  increasing with each update no problem occurs, you can trigger this behaviour with the minimal example provided, specifically by selecting numbers in the multi select of the example , going from "2" to "3" is no problem, going back to "2" triggers the error, going into the browsers developer tools will reveal a "size mismatch" error as soon as you trigger it.


the problem line is 
```
plot.renderers.extend([sc])
```
Fixing this problem is as easy as removing that line, but found it curious since this wasn't a problem in the previous bokeh releases I've tested so far.

```python
from bokeh.models.widgets import MultiSelect
from bokeh.layouts import layout
from bokeh.plotting import figure, curdoc
from bokeh.models import ColumnDataSource
import numpy as np
import pandas as pd

#Column Data Sources
source_data = ColumnDataSource(
    data=dict(X=[], Y=[], main_legend=[], color=[], marker=[], alpha=[])
)

def update(attr, old, new):
    if plot_multi_select.value:
        val = int(plot_multi_select.value[0])
    else:
        val = 1
    x = np.linspace(0,val,val*10)
    y  = np.zeros_like(x)
    y = x**val
    df = pd.DataFrame()
    df["X"] = x
    df["Y"] = y
    df["color"] = "blue"
    df["alpha"] = 0.7
    df["marker"] = "circle"
    df["main_legend"] = str(val)
    source_data.data.update(ColumnDataSource(data=df).data)

#Multiselect
plot_multi_select = MultiSelect(
    title="Plot Select:",
    value=[],
    options=["2","3","4"],
    height=200
)
plot_multi_select.on_change("value", update)
#main plot
plot = figure(
    plot_width=1270,
    plot_height=900,
    tools="pan,wheel_zoom,box_zoom,reset,save",
    toolbar_location="left",
    output_backend="canvas",
)
# Plot scatter artist
sc = plot.scatter(
    x="X",
    y="Y",
    source=source_data,
    legend_field="main_legend",
    size=10,
    color="color",
    marker="marker",
    alpha='alpha',
)


plot.renderers.extend([sc])

l = layout(children=[[plot,plot_multi_select]])

curdoc().add_root(l)

```
user: dacts23
issue_11052: state: open
title: General WebGL improvements
body: WebGL improvements specific to lines and markers are covered by issues #11050 and #11051 respectively.  Here are grouped improvements that will affect all WebGL (lines, markers and other future graphics primitives).

- [x] 10k examples have rendering artifacts.  Possibly different alpha-blending defaults on different platforms. (PR #11070)
- [ ] Cross-backend testing (WebGL vs canvas).
- [ ] Add WebGL to existing canvas and svg only tests.
- [x] Look into `pixel_ratio != 1`.
- [x] Reuse of `Float32Array`s rather than recreating frequently. (PRs #11277 and #11343).
user: ianthomas23
issue_11051: state: open
title: Complete WebGL marker rendering
body: Following the switch to ReGL, improvements are needed to complete WebGL marker rendering. Here they are grouped together for convenience, to be ticked off as they are completed.

- [x] Correct instanced rendering to use a mesh rather than `gl_PointSize` which can be limited on some devices (PR #11098).
- [x] Some marker types have incorrect size and/or color (PR #11098).
- [x] Add missing marker types (PR #11098).
- [x] Look into keeping full-length visual properties on GPU and passing in indices to render, to reduce CPU <-> GPU chatter (PR #11098).
- [x] Remove temporary `Circle` fix.  Best to do this after previous item (PR #11849).
- [x] Check support for missing coordinates (NaN).
- [x] Hatching.
- [ ] Support all line options including dashes and joins.
user: ianthomas23
issue_11050: state: open
title: Complete WebGL line rendering
body: Following the switch to ReGL, improvements are needed to complete WebGL line rendering.  Here they are grouped together for convenience, to be ticked off as they are completed.

- [x] Antialiasing on thin lines is not quite correct.  Best seen on lines that are nearly horizontal or vertical (PR #11252)
- [x] Dashes with butt end caps have slightly dodgy antialiasing at joins. (PR #11814)
- [ ] Dashes with round or square end caps need rethink at joins.
- [ ] Spurious end caps on mobile devices.  May be due to lower floating-point precision in shaders.
- [x] Overlapping lines have artifacts, probably alpha-related. Look into discarding pixels instead of alpha=0?  (PR #11252)
- [x] Line dash pattern can have odd number of entries; repeat (concatenate) if so. (PR #11819)
- [x] Increasing line10k example to 100k points shows rendering artifacts, related to shader precision and nearly-parallel line segments (PR #11108)
- [x] Handling of NaNs.


user: ianthomas23
issue_11044: state: open
title: [BUG] plotting text via figure.text() gives BAD_COLUMN_NAME for text_font key
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
bokeh 2.3.0
python 3.7.4
MacOS Big Sur
Chrome 88.0.4324.192

#### Description of expected behavior and the observed behavior
In bokeh 2.3.0, if I use the figure.text() method to plot text then setting text_font="arial" gives an error:

```
ERROR:bokeh.core.validation.check:E-1001 (BAD_COLUMN_NAME): Glyph refers to nonexistent column name. This could either be due to a misspelling or typo, or due to an expected column being missing. : key "text_font" value "arial" [renderer: GlyphRenderer(id='1037', ...)]
```

and the text is plotted in (I presume) the default font.

In bokeh 2.2.3 there is no error and the text is plotted in Arial.

Example code:

```
    from bokeh.plotting import figure, ColumnDataSource
    from bokeh.models import Range1d
    from bokeh.io import show
    import pandas as pd

    output_file("test_text.html")
    plot = figure(
        plot_width=500,
        plot_height=500,
        x_range=Range1d(0, 200),
        y_range=Range1d(0, 100))
    plot.text(
        x="x",
        y="y",
        text="text",
        text_align="center",
        text_font_size="30px",
        text_font="arial",
        text_font_style="normal",
        source=ColumnDataSource(
            pd.DataFrame.from_records(
                [
                    dict(
                        x=100,
                        y=50,
                        text="Hello",
                        color="black",
                    )
                ]
            )
        ),
    )
    show(plot)
```

JavaScript console output:

```
[bokeh] setting log level to: 'info'
bokeh-2.3.0.min.js:183 [bokeh] attempted to retrieve property array for nonexistent field 'arial'
uniform @ bokeh-2.3.0.min.js:183
bokeh-2.3.0.min.js:165 [bokeh] document idle at 35 ms
```
Compared to this for 2.2.3:
```
[bokeh] setting log level to: 'info'
bokeh-2.2.3.min.js:166 [bokeh] document idle at 28 ms
```


#### Screenshots
![Screen Shot 2021-03-12 at 10 35 18](https://user-images.githubusercontent.com/18192472/110928312-a70f7e00-831e-11eb-9ff4-1221d677db8d.png)

compared to 2.2.3:
![Screen Shot 2021-03-12 at 10 35 49](https://user-images.githubusercontent.com/18192472/110928372-b8f12100-831e-11eb-93f7-21a835127398.png)

user: kiramt
issue_11036: state: open
title: [BUG] Making axis range padding persistent requires changing `._initial_range_padding` as well
body: Version: 2.2.3 and 2.3
Browsers: Chrome, Brave, Safari

Setting `plot.x_range.start` and `plot.x_range.end`, or `plot.x_range.range_padding` to adjust whitespace around glyphs on an X axis (or the corresponding properties for a Y axis) is not persistent across resets. In the Python library, the values for these properties are persistent when a figure is reset. 

To achieve similar persistent behavior in BokehJS, I have to set values for both `plot.x_range.range_padding` and `plot.x_range._initial_range_padding`.

A complete, minimal example is given via CodePen at [bokehjs_range_padding](https://codepen.io/oviquezr/pen/poNdPYX). The example contains two images, where one had only `.range_padding` updated, and the other had both `.range_padding` and `._initial_range_padding` adjusted. Use the reset button to observe the behavior difference.

Issue is related to discourse topic: [Differences in BokehJS vs Python : expected usage](https://discourse.bokeh.org/t/differences-in-bokehjs-vs-python-expected-usage/7313)
user: oviquezr
issue_11032: state: open
title: [FEATURE] Make the on-axis panning option noticeable
body: Hi
Following #11027, please make the on-axis panning noticeable. That behavior is unknown because the mouse cursor is just the regular pointer, and not a horizontal/vertical arrows that indicate that panning (or any special behavior, in fact) is possible.

Thanks.

user: erezinman
issue_11027: state: open
title: [FEATURE] Revamp Bokeh's pan & zoom
body: As a longtime user of Bokeh, I find it beautifully written and very useful. However, as far as I'm concerned, the competition (Plotly) has one major feature that Bokeh lacks, and that feature makes Plotly invaluable for the purpose of data-exploration & data-analysis. That feature is Plotly's pan & zoom capabilities. Plotly allows simple pan & zoom in a single axis without the need to write & press on multiple buttons. That behavior is shown in the GIF below. 

![Peek 2021-03-09 13-47](https://user-images.githubusercontent.com/4324329/110475183-3a5f6e00-80e9-11eb-92b2-da5dcc009e2e.gif)

A description of what is seen in this GIF (and the features shown in it):
1. The click-behavior is zoom (but it will work similarly with pan or any other selected tool).
2. Zooming is can be restricted to one axis without selecting a specialized tool.
3. Mouse-dragging the middle of an axis (could be anywhere on the axis) allows to pan in just one direction. 
4. Mouse-dragging the edge of an axis allows an interactive change of zoom (especially zoom-out) in one direction.

Another useful I forgot to record is:

5. Mouse-dragging from the corner allows for change of zoom (especially zoom-out), where the opposite corner is anchored.

Such behavior or similar will be extremely useful in Bokeh. If backward-compatibility is a concern, let the figure have a flag named "extended_pan_and_zoom_behavior"  or "exploration_mode" that's set to False by default. Another possibility is to have a separate flag for every behavior (that is - "axis_pan", "axis_zoom", and "axis_corner_zoom" in the figure, and "allow_one_dimensional_zoom" in the zoom-tool).

If this behavior is copyrighted in some way, it may possible to find other ways to provide this functionality intuitively (perhaps using the arrows, using shift/alt/control keys, or some other way).

Thank you for your consideration.
user: erezinman
labels: tag: tools: Related to the toolbar and individual tools
issue_10998: state: open
title: [BUG] legend click policy 'hide' has unexpected behavior on automatic-grouping
body: #### ALL software version info 
bokeh: v2.3.0
python: 3.6+

#### Description of expected behavior and the observed behavior
I was checking the automatic-grouping using this example from the [docu](https://docs.bokeh.org/en/latest/docs/user_guide/annotations.html#automatic-grouping-python-side).

#### The data:
```
import pandas as pd

from bokeh.io import show, outut_notebook
from bokeh.models import ColumnDataSource
from bokeh.palettes import RdBu3
from bokeh.plotting import figure
outut_notebook()

c1 = RdBu3[2] # red
c2 = RdBu3[0] # blue
source = pd.DataFrame(dict(
    x=[1, 2, 3, 4, 5, 6],
    y=[2, 1, 2, 1, 2, 1],
    color=[c1, c2, c1, c2, c1, c2],
    label=['hi', 'lo', 'hi', 'lo', 'hi', 'lo']
))
```
#### How it is now:
I just added one line to change the click policy of the legend. Then clicking on the legend, all elements were hidden.

```
p = figure(x_range=(0, 7), y_range=(0, 3), plot_height=300, tools='save')
# legend field matches the column in the source
p.circle( x='x', y='y', radius=0.5, color='color', legend_group='label', source=source)
p.legend.click_policy="hide"
show(p)
```
![real](https://user-images.githubusercontent.com/68053396/109947316-781f5980-7cd9-11eb-8f06-e59e8eb3c317.gif)

#### Expectation
I was expecting, that the grouping works more like this:
```
p = figure(x_range=(0, 7), y_range=(0, 3), plot_height=300, tools='save')
# legend field matches the column in the source
grouper = source.groupby('label')
for name in source['label'].unique():
    p.circle( x='x', y='y', radius=0.5, color='color', legend_label=name, source=grouper.get_group(name))
p.legend.click_policy="hide"
show(p)
```

![expected](https://user-images.githubusercontent.com/68053396/109947568-bf0d4f00-7cd9-11eb-967d-d08d76b226d2.gif)

user: mosc9575
issue_10996: state: open
title: Warning label for unsupported browsers
body: Especially with #9731 (drop support for legacy web browsers), it would be good to have a way to notify (end) users if they are using an unsupported (i.e. outdated) browser. As far as I understand, currently there would only be error messages in the browser's console.

This could take the form of a message that is displayed in place of a Bokeh visualization in an unsupported browser, for example.
user: tcmetzger
issue_10995: state: open
title: Unified Bokeh model for text rendering
body: During a discussion about adding support for MathJax-based rendering of LaTeX, @bryevdv, @brendancol, @IuryPiva, and I had the idea that it might be helpful to have a unified text model to share as much code as possible between Bokeh's various text elements, such as labels or titles. 

This would help implement LaTeX support and might also be helpful for other goals, such as improving accessibility or supporting line breaks.
user: tcmetzger
issue_10994: state: open
title: Allow Bokeh to generate a "snapshot" URL
body: Analytics dashboards often contain filters & selections that create very personalized views of the data. At the moment there is no built-in mechanism or documented approach in Bokeh that allows a user to share this state with a second user. 

A conceptually simple solution would be to encode the full or a subset of the document's state in the url. A second user would then visit this url, allowing Bokeh to set up the configuration on the fly. 

Some extra discussion on: https://discourse.bokeh.org/t/setting-url-parameters/7325/8
user: rsdenijs
issue_10992: state: open
title: Raise visibility of downstream tests
body: Currently the downstream tests are configured to never fail, in order to prevent coupling our build to factors that are outside our control. But this makes it harder to be know when there are downstream issues, because seeing the actual results requires digging in to the build logs. 

We would like to be able to have a quick visual indication whether the job actually passed or failed directly in the PR, but also prevent the job from ever failing a build. 

The [`continue-on-error`](https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstepscontinue-on-error) flag may satisfy our needs but will require some investagtion. 
user: bryevdv
issue_10985: state: open
title: [FEATURE] Give users control over table's cell paddings so rows/columns can be colored continuously
body: As described at https://discourse.bokeh.org/t/datatable-htmltemplateformatter-cell-size/6763:

Currently, one can set the background color and other CSS style elements of DataTable cells using the HTMLTemplateFormatter. However, this assigns a child <div class> to the parent <div class slick-cell> which has padding set to 4 px and border set to 0.25 px. Thus, there’s always going to be a bit of white background behind the color, as seen in the answer to https://stackoverflow.com/questions/50996875/how-to-color-rows-and-or-cells-in-a-bokeh-datatable 5.

It would be nice to be able to color an entire row without that white margin showing up. With the parent having that padding, I don’t think this is possible. One could set the width of the child to something like 110% in the HTMLTemplateFormatter, but this would only push the background color in one direction.

One possibility would be to set the parent’s padding and border to 0 px, and then the child (which the user gets control over with HTMLTemplateFormatter) gets to be anywhere the user wants it. So we'd change https://github.com/bokeh/bokeh/blob/b56543346bad8cdabe99960e63133619feaafbb7/bokehjs/src/less/widgets/tables.less 1 lines 49, 50, and 52 to 0 px.

However, this does make the default look not look as good, so perhaps there's a more elegant solution that allows the default to look good while still giving the user the control to set the parent's padding and border.
user: EfremBraun
issue_10984: state: open
title: Update GraphRenderer EdgesOnly documentation
body: Documentation update request related to the `EdgesOnly` GraphHitTestPolicy to convey the following (rephrase as appropriate):
"On EdgesOnly inspection policy for network graphs, when using Hovertools, use "line_policy='interp'" to HoverTool args to get tooltip to display on edge and not nearest node."

`EdgesOnly` can be found here:
http://docs.bokeh.org/en/latest/docs/reference/models/graphs.html?highlight=edgesonly#bokeh.models.graphs.EdgesOnly

This may or may not be the most appropriate place to put it; maybe it goes at a higher level. I would think we'd also want to update the User Guide section on network graphs:
http://docs.bokeh.org/en/latest/docs/user_guide/graph.html?highlight=network%20graph
user: ghost
issue_10976: state: open
title: Wrong data shape for a glyph results in an exception
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
bokeh 2.3, python 3.7, OS windows, browser chromium

#### Description of expected behavior and the observed behavior
I expect the code below not to lead to an error in the front-end, which was the case up to Bokeh 2.1.1
Since Bokeh 2.2 it gives an error, see screenshot below.

@mattpap mentioned on the discourse page it should not give this error in any case: https://discourse.bokeh.org/t/bokeh-2-2-page-does-not-render-need-help-debugging/6221

When this error occurs it stops rendering the rest of the page.

I guess the dict does not have the structure that the columndatasource component expects, but I would expect an error on the backend side then.

#### Complete, minimal, self-contained example code that reproduces the issue

```
from bokeh.plotting import curdoc, figure
from bokeh.models import ColumnDataSource

source = ColumnDataSource(data={'xs': [1, 1, 1],
        'ys': [3, 3, 3],
        'labels': ['one', 'two', 'three']})

p = figure(title="Plot", name="p",
            match_aspect=False, sizing_mode="stretch_both")

p = figure(width=600, height=300)
p.multi_line(xs='xs', ys='ys', legend_group='labels', source=source)

curdoc().add_root(p)
```

#### Stack traceback and/or browser JavaScript console output & screenshots or screencasts of the bug in action
![image](https://user-images.githubusercontent.com/7702207/109487942-2f6f6280-7a85-11eb-9199-ccb6bbbb1afb.png)

user: harmbuisman
issue_10974: state: open
title: [FEATURE] Use a colorblind friendly color scheme in documentation
body: ... rather than red/green, which is hard to distinguish for Daltonian people.

Reasoning: When reading the docs, I first didn't understand the [annotations example](https://docs.bokeh.org/en/latest/docs/first_steps/first_steps_3.html#using-annotations). It seemed that the annotation was missing, since I only saw a blue line on a uniform background. Only when looking at the code did I realize that there was a red/green pattern in the background of the plot.

Note that, according to [Wikipedia]: Red–green color blindness affects up to 8% of males and 0.5% of females of Northern European descent.

I've prepared a pull request.

[Wikipedia]: https://en.wikipedia.org/wiki/Color_blindness
user: Sjlver
labels: tag: accessibility: Improvements to Bokeh's accessibility
issue_10968: state: open
title: [BUG] Widgets fail to update when associated to multiple layouts
body: Under `bokeh==2.3.0`

When associating  a `Div`widget to two columns, updated to its `text` property fail to render. 
I am not 100% sure this is expected to be supported, but if not Bokeh should probably warn or error.


```python
"""Example of updating a Div box based of tab selection"""
from bokeh.layouts import column
from bokeh.models import Tabs, Panel, Div
from bokeh.plotting import curdoc

info = Div(text='Nothing was selected yet')

def update(attr, old, new):
    info.text = f'selected tab {new}'

# Info box fails to update
tab1 = Panel(child =info, title= 'Tab 0')
tab2 = Panel(child =info, title= 'Tab 1')
tabs = Tabs(tabs=[tab1, tab2])
tabs.on_change('active', update)
curdoc().add_root(column(tabs))

# Variants

# Info box updates as expected above the tabs
# tab1 = Panel(child = column(), title= 'Tab 0')
# tab2 = Panel(child = column(), title= 'Tab 1')
# tabs = Tabs(tabs=[tab1, tab2])
# tabs.on_change('active', update)
# curdoc().add_root(column(info, tabs))

# Info box updates as expected when one of the panels is selected
# tab1 = Panel(child =info, title= 'Tab 0')
# tab2 = Panel(child = column(), title= 'Tab 1')
# tabs = Tabs(tabs=[tab1, tab2])
# tabs.on_change('active', update)
# curdoc().add_root(column(tabs))



```

Output after selecting Tab 1:
<img width="167" alt="image" src="https://user-images.githubusercontent.com/3237939/109404368-53626500-791a-11eb-850b-96790b277c41.png">


user: rsdenijs
issue_10962: state: open
title: Add examples/bokehjs directory
body: There are a handful of basic examples under `bokehjs` that are mostly used for testing, but it would be good to consolidate all user-facing "documentation" examples in one place, i.e. the main `examples` directory. 

Some ideas for content there:

* toy examples using different build systems / bundlers
* toy examples integrating BokehJS in react and angular
* examples using the `Bokeh.plotting` API
* examples embedding JSON from external sources
* node app usage?

cc @bokeh/dev for other thoughts/ideas
user: bryevdv
issue_10960: state: open
title: [FEATURE] Expose gmap zoom level and long/lat as read-only property in python
body: As per this discourse thread https://discourse.bokeh.org/t/get-current-gmap-zoom-level-and-long-lat/7156/2 I'd like to propose to add a feature whereby the current gmap zoom level and lat/long can be read from python.

**Is your feature request related to a problem? Please describe.**
We allow the user to pan/zoom but for some functionality we'd like to know the current zoom and lat/long. E.g. we have a button to switch between satellite and roadmap via assigning new GmapOptions but since we don't know user zoom/lat/long we have to set some standard values which is confusing to the user. Also we'd like to implement some more complex logic based on the current user zoom level.

**Describe the solution you'd like**
Being able to access current zoom and lat/long via something like `my_gmap.map_options.zoom` would be useful.

**Describe alternatives you've considered**
I didn't find any workaround.

**Additional context**
-


I'd be happy to try and work on this feature given some guidance. Thanks!
user: johanneshk
issue_10958: state: open
title: Derive CHANGELOG from release notes files
body: discussion ref: https://github.com/bokeh/bokeh/issues/10949

* Add perpetual `unreleased.rst` (per release branch) to collect new changes in during a release cycle
* Automate copying the content of `unreleased.rst` into a new `x.y.z.rst` for release
* At release, completely re-generate `CHANGELOG` by concatenating all `x.y.z.rst` in reverse order

Incidental notes:

* `milestones.py` script can be simplified (only needed to check tracker label hygiene)
* format for release notes `.rst` files may need some tweeking
  - better ISO dates for release
  - uniform section/structure
  - changes to make plaintext CHANGELOG version as nice as possible
  - release notes page can potentially just be a `literalinclude` of CHANGELOG the the appropriate tag

cc @tcmetzger for any input (esp thoughts about the best structure of the release notes files)

@p-himik what (if anything) should we do about `unreleased.rst` besides just having it? It should not go in the website release notes or generated CHANGELOG because those are published *right at release*, when there is nothing remaining in `unreleased.txt`. In principle, it would be nice to link to it somewhere, but there are *multiple versions* of the file, on on each minor release level branch. I'm not sure there is any simple way to raise visibility of it (them).
user: bryevdv
issue_10905: state: open
title: [FEATURE] Add support for 'prepend' and 'delete' for ColumnDataSource
body: Current version of ```ColumnDataSource``` supports only ```stream()``` and ```patch()``` methods for modifying source data, where ```stream()``` appends new data to the end of a stream and ```patch()``` modifies data in place. However there are cases when one also needs to prepend new data to the begining of stream and/or deleting no longer useful data (both from begining and end of the stream).

Also please keep in mind, that ```ColumnDataSource``` columns can have other datatypes than just ```list``` and ```numpy.ndarray``` if created from Pandas ```DataFrame``` (like ```IntegerArrays``` or ```Categorical```).

Obviously, new methods would be probably preferable, however one could achieve the same results also via ```patch()``` method as alternative aproach.
```
# prepend
column_data_source.patch({column_name: [(slice(None, 0), new_data)]})
# delete from start
column_data_source.patch({column_name: [(slice(None, target_position), [])]})
# delete from end
column_data_source.patch({column_name: [(slice(target_position, None), [])]})
# append (like a stream())
column_data_source.patch({column_name: [(slice(old_length+1, None), new_data)]})
```
This would however work only on ```list``` so to add support for different data types, the ```patch()``` method would have to be modified accordingly.
user: rad83
issue_10904: state: open
title: [BUG] Stream data with more Pandas data types
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
python: 3.9.1
bokeh: 2.2.3
numpy: 1.20.0
pandas: 1.2.1

#### Description of expected behavior and the observed behavior
Method ```stream()``` of ```ColunmDataSource``` expect the columns data to be either ```list``` or ```numpy.ndarray```. However if created from Pandas ```DataFrame```, the columns can have different data types. For instance it can be ```pandas.core.arrays.integer.IntegerArray``` or ```pandas.core.arrays.categorical.Categorical```. In both those cases attempt to stream new data to such source will raise ```AttributeError``` complaining about missing ```extend``` attribute (as it expect it to be list).

#### Complete, minimal, self-contained example code that reproduces the issue
```
import pandas as pd
from bokeh.models import ColumnDataSource as CDS

df = pd.DataFrame({'a': [1, 2, 3], 'b': [1, 2, 3], 'c': [1, 2, 3]})
df.b = df.b.astype('Int64')
df.c = df.c.astype('category')

cds = CDS(df)
cds.stream({'index': [3], 'a': [4], 'b': [pd.NA], 'c': [1]})
```

#### Stack traceback and/or browser JavaScript console output
```
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.9/site-packages/bokeh/models/sources.py", line 415, in stream
    self._stream(new_data, rollover)
  File "/usr/lib/python3.9/site-packages/bokeh/models/sources.py", line 527, in _stream
    self.data._stream(self.document, self, new_data, rollover, setter)
  File "/usr/lib/python3.9/site-packages/bokeh/core/property/wrappers.py", line 423, in _stream
    L.extend(new_data[k])
AttributeError: 'IntegerArray' object has no attribute 'extend'
```

#### Code responsible for the streaming
(bokeh/core/property/wrappers.py)
```
def _stream(self, doc, source, new_data, rollover=None, setter=None):
    ...
    for k, v in  new_data.items():
        if isinstance(self[k], np.ndarray) or isinstance(new_data[k], np.ndarray):
            data = np.append(self[k], new_data[k])
            if rollover and len(data) > rollover:
                data = data[-rollover:]
            dict.__setitem__(self, k, data)
        else:
            L = self[k]
            L.extend(new_data[k])
            if rollover is not None:
                del L[:-rollover]
    ...
```
user: rad83
issue_10903: state: open
title: [BUG] URL format in tile_providers.py does not match format requirement in wmts_tile_source.ts 
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)

Python 3.8.0
bokeh==2.2.3
Chromium Version 87.0.4280.66 (Official Build) Built on Ubuntu , running on Ubuntu 18.04 (64-bit)
Linux glittertind 4.15.0-135-generic #139-Ubuntu SMP Mon Jan 18 17:38:24 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

#### Description of expected behavior and the observed behavior

Expected behavior: It's possible to change the background map used for a plot to another map, then back to a standard map.
Observed behavior: When changing back to `bokeh.tile_providers.get_provider(bokeh.tile_providers.CARTODBPOSITRON)`, map tiles fail with 404 due to bad urls.

The [JS wmts tile source code](https://github.com/bokeh/bokeh/blob/7079a15c82e3466afaa28955b75e0af929128246/bokehjs/src/lib/models/tiles/wmts_tile_source.ts#L23) clearly requires uppercase {X}, {Y} and {Z}, while the [tile provider urls](https://github.com/bokeh/bokeh/blob/branch-2.3/bokeh/tile_providers.py#L180) uses lowercase {x}, {y}, {z}.

This should never have worked! I'm unsure why it works when you initially add `bokeh.tile_providers.get_provider(bokeh.tile_providers.CARTODBPOSITRON)` to the map.

#### Complete, minimal, self-contained example code that reproduces the issue

In a button click callback or similar:

```
tileSource = bokeh.tile_providers.get_provider(bokeh.tile_providers.CARTODBPOSITRON)
figure.renderers = (  [bokeh.models.renderers.TileRenderer(tile_source=tileSource)]
                                + [renderer for renderer in figure.renderers
                                   if not isinstance(renderer, bokeh.models.renderers.TileRenderer)])
```

#### Stack traceback and/or browser JavaScript console output

In network tab of the developer tool:

```
https://tiles.basemaps.cartocdn.com/light_all/%7Bz%7D/%7Bx%7D/%7By%7D.png 404
Error: z/x/y must be numbers!

```

#### Screenshots or screencasts of the bug in action

user: redhog
issue_10902: state: open
title: [BUG] defining colors is inconsistent
body: ### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
Bokeh 2.3.0dev13 (Python 3.8.6, Win10)

### Description of expected behavior and the observed behavior
Expected: Defining and using colors should be consistent across all different ways to define colors (as much as possible)
Observed: Some ways of defining colors work different than others

### Complete, minimal, self-contained example code that reproduces the issue

**Example 1: defining single colors with CSS color strings**

A single `rgb()` color string works:
```
from bokeh.plotting import figure, show

x = [1,2,3]
y = [1,2,3]
col = "rgb(200 0 0)"

p = figure(title = "Using single rgb() color value works")
p.circle(x, y, radius=.25, color=col)
show(p)
```
However, a single `hsl()` color string does not work:
```
from bokeh.plotting import figure, show

x = [1,2,3]
y = [1,2,3]
col = "hsl(60deg 100% 50% / 1.0)"

p = figure(title = "Using single hsl() color value fails")
p.circle(x, y, radius=.25, color=col)
show(p)
```
hsl() color strings as items in a list / array do work, therefore I guess that adding a hsl regex to `__init__()` in Bokeh.core.properties.Color might resolve this problem.

**Example 2: defining single colors with a 32-bit unsigned integer**

Similarly, using a single 32-bit unsigned integer does not work:
```
from bokeh.plotting import figure, show

x = [1,2,3]
y = [1,2,3]
col = 0xff0000ff

p = figure(title = "Using single 32-bit int color fails")
p.circle(x, y, radius=.25, color=col)
show(p)
```
32-bit unsigned integers as items in a list do work, so the rexpective data type might also have to be added to `__init__()` in Bokeh.core.properties.Color 

EDIT: I used to have a third example here, but as @mattpap correctly pointed out, this behaviour is consistent with CSS4 syntax.
user: tcmetzger
issue_10890: state: open
title: [FEATURE] Per-item styles and disabling menu items in a Dropdown
body: **Is your feature request related to a problem? Please describe.**
Sometimes the menu items in a Dropdown need more info than just the name, e.g. some items that should not really be selected because of some other conditions (gray color?), while some other items can be selected but are dangerous (red color?) or recommended configuration (green?), or font style like italic, bold ... maybe some items must not be selected so they can be disabled, although they are still shown in the list.

**Describe the solution you'd like**
It would be nice to pass these options in the tuples that describe each item, so that each tuple contains the item text together with some style considerations affecting that particular item, together with the enabled/disabled state.

**Describe alternatives you've considered**
Some of these options (color, bold, italic, maybe font) could be added to the Dropdown constructor in Python. For a more general approach, they could use css_classes, or some dynamic mechanism to be applied per-item, similar to the existing Formatters for table cells (which in my opinion can be a little bit too involved, that's why I think the most simple and commonly used options like text color, background color, bold/italic and font could be arguments in the constructor, or in the tuple representation of the items).

**Additional context**
Example: selecting tables from a database to display the content, but some of the tables are forbidden due to permissions, etc. Or choices that are possible but discouraged.

user: olbapjose
issue_10889: state: open
title: [FEATURE] Automatically add info on deprecation to docs
body: When an object is deprecated, there currently is the `deprecated()` function to output a warning when the deprecated object is used.

From a documentation perspective, it would be helpful to include similar information in the reference guide as well. I currently see two ways this could be approached:

* expand the `deprecated()` function to not only generate console output but to also add information to the deprecated object's docstring.
* create a new decorator (such as `@deprecated`) that adds information to the deprecated object's docstring.

This idea came up on #10885 in a [comment](https://github.com/bokeh/bokeh/pull/10885#discussion_r568010612) by @bryevdv.
user: tcmetzger
issue_10882: state: open
title: Docs: Keeping "general" vs "dev" API docs separate
body: As discussed in #10879, elements of the "general" and the "dev" API are not consistently delineated in the docs (e.g. `bokeh.io.export_png` vs. `bokeh.io.export.export_png`).

As suggested by @bryevdv , the purpose of this issue is to discuss

- what do we want to happen in terms of UX for "general" vs "dev" API docs
- how can we implement this

In addition to `bokeh.io`, this might be relevant to most of the top level modules that are structured as a dir/__init__.py with submodules.
user: tcmetzger
issue_10872: state: open
title: [FEATURE] Document info about supported browser
body: Connected to #9731 and the discussion there, I propose adding information about which browsers are currently supported (or at least which minimum specs must be met). I'd be happy to open a pull request to include this information in the docs.
user: tcmetzger
issue_10868: state: open
title: [BUG] axis_label_text_align not working?
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)

Bokeh latest (2.2.3 at the time of writing)

#### Description of expected behavior and the observed behavior

It looks like the property `axis_label_text_align` is not working as expected (or at least not how I would have thought it should work).

#### Complete, minimal, self-contained example code that reproduces the issue

I basically want the label for the x axis to be on the right instead of at the center:

![bokeh_axis_label_align](https://user-images.githubusercontent.com/20630819/105742021-45947b00-5f3b-11eb-8031-5560cc806ceb.PNG)

In order to do so, please consider the following minimal example (can be copy-pasted in a notebook cell directly):

```python
from bokeh.io import output_notebook, show
from bokeh.sampledata.iris import flowers
from bokeh.plotting import Figure
from bokeh.models import ColumnDataSource

output_notebook()

cds = ColumnDataSource(data=flowers)

chart = Figure(title="Chart",
               width=600,
               height=400)

chart.scatter(x="petal_width",
              y="sepal_width",
              source=cds)

# Customize xaxis label:
chart.xaxis.axis_label = "x axis label"
chart.xaxis.axis_label_text_align = "right"

show(chart)
```

But the title is still centered alongside the x axis. 

Is this a bug?

Thank you!
user: julioasotodv
issue_10866: state: open
title: [BUG]Bokeh Client does not establish connection with server with self signed certificates
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
bokeh 2.2.3
python 3.9.1
windows 10

#### Description of expected behavior and the observed behavior
I try to establish a session through the client pull_session to a bokeh server launched with self signed certificates
I would expect to establish the session, but the client fails. 
I'm trying this as i'm trying to embed bokeh in flask and i want flask/bokeh to terminate the ssl 
I know it's not good for production but it is VERY better for testing.
SSL termination is required to interact with external authentication services like Auth0, so if you want to experiment with Auth0 locally you need an SSL. 
I think it's also useful in other contexts.
I think the issue is that the HTTPClient in the Bokeh Client doesn't catch the exception launched when ssl sees the self signed certificate, when i connect from browser the connection works well, if i try from curl without -k breaks

#### Complete, minimal, self-contained example code that reproduces the issue
**SERVER START**
python -m bokeh serve --allow-websocket-origin=* --use-xheaders --ssl-certfile easypcr-localhost.cert --ssl-keyfile easypcr-localhost.key --log-level debug ..\\samples_explorer

**CLIENT CODE** 
```
#tried with or without this with no effect
ssl._create_default_https_context = ssl._create_unverified_context
#--------------------------------------------------------

BOKEH_URL='https://localhost:5006/samples_explorer'

incoming_session_id = "abcdef" 
filename = 'V9NJQODGRQMQIUC78W70'
session = pull_session( url=BOKEH_URL,arguments=dict(filename=filename,incoming_session_id=incoming_session_id))
# generate a script to load the customized session
bokeh_script = server_session(session_id=session.id, url=BOKEH_URL )

```

#### Stack traceback and/or browser JavaScript console output
**CLIENT:**
Traceback (most recent call last):
  File "c:\DDCT\server\samples_explorer\test_bokeh_client.py", line 66, in <module>
    session = pull_session( url=BOKEH_URL,arguments=dict(filename=filename,incoming_session_id=incoming_session_id))
  File "C:\Users\lbarba\AppData\Local\Programs\Python\Python39\lib\site-packages\bokeh\client\session.py", line 120, in pull_session
    session.pull()
  File "C:\Users\lbarba\AppData\Local\Programs\Python\Python39\lib\site-packages\bokeh\client\session.py", line 381, in pull
    self.check_connection_errors()
  File "C:\Users\lbarba\AppData\Local\Programs\Python\Python39\lib\site-packages\bokeh\client\session.py", line 369, in check_connection_errors
    raise OSError("We failed to connect to the server (to start the server, try the 'bokeh serve' command)")
OSError: We failed to connect to the server (to start the server, try the 'bokeh serve' command)

**SERVER**
2021-01-24 11:39:04,562 SSL Error on 2572 ('127.0.0.1', 54888): [SSL: TLSV1_ALERT_UNKNOWN_CA] tlsv1 alert unknown ca (_ssl.c:1123)
2021-01-24 11:39:09,231 [pid 19848] 1 clients connected
#### Screenshots or screencasts of the bug in action

user: alucab
issue_10857: state: open
title: [FEATURE] Add the ability to format all the labels in a multilevel CategoricalAxis
body: **Is your feature request related to a problem? Please describe.**
I've created a plot which its axes are multilevel categorical axes. 
![image](https://user-images.githubusercontent.com/26378397/105036978-f4314b00-5a65-11eb-9402-38de9b958534.png)

Currently, I'm able to format only the labels of the categories that are on the lowest level ("Diameter", "Length" etc) and I would like to be able to format categories from all levels.  

**Describe the solution you'd like**
I'm not sure what's the best solution here.  
Maybe having a `labels_overrides` property that can be set to a dictionary that looks like:
```python
{
    0: {"E-Beam Deposition Conditions": "E-Beam Deposition Cond..."},
    1: {"Diameter": "DIAMETER"},
     ...
}
``` 
Where the keys 0,1 etc. refer to the category level (0-highest level). 

user: roiweinreb
issue_10849: state: open
title: DataTable: Make all selected rows in a DataTable non editable (until they are deselected)
body: The feature request is created basis the following discussion on Bokeh Discourse: https://discourse.bokeh.org/t/datatable-make-all-selected-rows-in-a-datatable-non-editable-until-they-are-deselected/6845

A feature is requested so that in a data table when a row is selected using the checkbox in data table index, the corresponding cells in the selected row should become Locked for editing. 

Please, find a use-case in the screenshot taken from bokeh discourse.


![Screen Shot 2021-01-18 at 1 07 27 PM](https://user-images.githubusercontent.com/20950585/104885510-27a0a680-598e-11eb-8689-ae4580814f6d.png)

user: garvk
issue_10844: state: open
title: DataTable cell editors are non-functional
body: DateEditor and IntEditor (at least) are not functional editors for DataTable TableColumns.

MRE:
```
from bokeh.models import ColumnDataSource, TableColumn, DateFormatter, DataTable, DateEditor, IntEditor
import datetime as dt
from bokeh.plotting import show

int_array = [i for i in range(0, 10)]
date_array = [dt.datetime.today() + dt.timedelta(days=i) for i in range(0, 10)]
cds = ColumnDataSource(data={'ints': int_array, 'dates': date_array})

columns = [
    TableColumn(field="ints", title="Int", editor=IntEditor()),
    TableColumn(field="dates", title="Date", formatter=DateFormatter(), editor=DateEditor())
]

dt = DataTable(columns=columns, source=cds, editable=True)

show(dt)
```

Actual behavior: Cells in both columns act like basic StringEditors.

Expected behavior: IntEditor should show a spinner-style selector for ints; DateEditor I believe is supposed to be a date-picker, calendar-type input.

(ref:[ Discourse topic](https://discourse.bokeh.org/t/how-to-use-dateeditor/7056/4))
user: ghost
issue_10816: state: open
title: Deprecate OpenURL
body: I'd like to propose deprecating [`OpenURL`](https://docs.bokeh.org/en/latest/docs/user_guide/interaction/callbacks.html#openurl) (and potentially `Taptool.callback` as well). It is the last "ad-hoc" callback action. Given that actually opening a URL in any `CustomJS` callback is a one-liner in JavaScript I don't see the need to maintain this bulky class just for that. We might, however want to expose the `replace_placeholders` function better, which would be useful for custom tooltip formatting anyway.

Thoughts @bokeh/dev ? 
user: bryevdv
issue_10812: state: open
title: Cannot import (fast-components) module in bokeh extension - "Export" statement may only appear at top level
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)

Bokeh: 2.2.3
Python: 3.7.8
OS: Win

#### Context

I'm trying to develop a custom bokeh extension in typescript using the [Fast.design](https://www.fast.design/) web components.

#### Description of expected behavior and the observed behavior

I would expect to be able to `npm install --save @microsoft/fast-components` and then add the following 3 lines of code to my `index.ts` file

```typescript
import { FASTDesignSystemProvider, FASTButton } from "@microsoft/fast-components";
FASTDesignSystemProvider;
FASTButton;
```

But when I run `bokeh build` I get the error

```bash
"Export" statement may only appear at the top level
    at minify
```

#### Complete, minimal, self-contained example code that reproduces the issue

[bokeh-fast.zip](https://github.com/bokeh/bokeh/files/5760791/bokeh-fast.zip)

You can reproduce the bug by unzipping the file and then from the root of the project running

```bash
npm install
pip install bokeh==2.2.3
bokeh build
```

The .zip file contains a README file describing the minimal steps I went through to initially setup the project.

#### Stack traceback and/or browser JavaScript console output

```bash
$ bokeh build
Working directory: C:\repos\private\bokeh-fast
Using different version of bokeh, rebuilding from scratch.
Running npm install.
audited 63 packages in 1.079s

3 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

Using C:\repos\private\bokeh-fast\tsconfig.json
Compiling TypeScript (1 files)
Linking modules
Error: C:\repos\private\bokeh-fast\node_modules\@microsoft\fast-components\dist\esm\index.js:1:4: "Export" statement may only appear at the top level
    at minify (c:\repos\private\bokeh-fast\.venv\lib\site-packages\bokeh\server\static\js\compiler.js:150331:19)
    at c:\repos\private\bokeh-fast\.venv\lib\site-packages\bokeh\server\static\js\compiler.js:149953:56
    at Array.map (<anonymous>)
    at artifacts (c:\repos\private\bokeh-fast\.venv\lib\site-packages\bokeh\server\static\js\compiler.js:149947:32)
    at Linker.link (c:\repos\private\bokeh-fast\.venv\lib\site-packages\bokeh\server\static\js\compiler.js:149963:50)
    at Object.build (c:\repos\private\bokeh-fast\.venv\lib\site-packages\bokeh\server\static\js\compiler.js:148834:32)
    at main (c:\repos\private\bokeh-fast\.venv\lib\site-packages\bokeh\server\static\js\compiler.js:214:46)
    at Object._ (c:\repos\private\bokeh-fast\.venv\lib\site-packages\bokeh\server\static\js\compiler.js:246:5)
    at require (c:\repos\private\bokeh-fast\.venv\lib\site-packages\bokeh\server\static\js\compiler.js:94:23)
    at c:\repos\private\bokeh-fast\.venv\lib\site-packages\bokeh\server\static\js\compiler.js:105:16
```

user: MarcSkovMadsen
issue_10802: state: open
title: [BUG] Range padding incorrect for multiple rect glyphs
body: Based on discussion on [discourse](https://discourse.bokeh.org/t/how-to-adjust-automatic-scaling-on-y-axis-when-using-rect-glyph/6958) I open this bug report.

I have tried to use the `rect` glyph to make bar plot where the x-axis is `Factorrange` and the y-axis is normal `DataRange1d`. It works OK except I observe a lot of padding below the bars. On the screenshot below I have a comparison of `vbar` (top) and `rect` using the same data. Both got `range_padding` of 0.1 and `range_padding_unit` of % and automatic scaling. Adjusting the value of `range_padding` does not help since it seems the padding is offset from the center of the highest bar.

I use bokeh ver 2.2.3, python 3.8.1

```
from bokeh.io import output_file, show, save
from bokeh.plotting import figure
from bokeh.layouts import column

output_file("bars_vbar_vs_rect.html")

fruits = ['Apples', 'Pears', 'Nectarines', 'Plums', 'Grapes', 'Strawberries']
counts = [5, 3, 4, 2, 4, 6]
count_mid = [item*0.5 for item in counts]

def fig(title):
	p = figure(
		x_range = fruits,
		plot_height = 250,
		title = title,
		toolbar_location = None,
		tools = ""
	)
	p.xgrid.grid_line_color = None

	return p

def rng_info(rng):
	print(rng)
	print(rng.bounds)
	print(rng.range_padding)
	print(rng.range_padding_units)


p1 = fig("Fruit Counts - vbar glyph")
p1.vbar(x = fruits, top = counts, width = 0.9)

rng_info(p1.y_range)

p2 = fig("Fruit Counts - rect glyph")
p2.rect(x = fruits, y = count_mid, width = 0.9,	height = counts)

rng_info(p2.y_range)

save(column(p1, p2))
```

<img width="615" alt="Skærmbillede 2020-12-28 kl  17 15 26" src="https://user-images.githubusercontent.com/33606696/103265506-027fd000-49ae-11eb-8e97-3e783490c947.png">


user: carve11
issue_10795: state: open
title: Examples of Bokeh extensions using React and Vue
body: A lot of awesome js libraries only work if you know how to use React or Vue. 

Currently there is no example (I believe) of using React or Vue with Bokeh Extensions. This makes the barrier of entry high for creating Bokeh Extensions using this huge resource. 

This might also limit the number of contributors to Bokeh (hypothesis) because this is what a large proportion of js developers use today.

I also believe that Bokeh Extensions development has a lot of boiler plate code and it seems some of the alternative frameworks like Streamlit and H2O AI have easier backend-frontend syncing of events and properties. And they can use nice "templating" where Bokeh extensions is built in a more traditional way.

Personally I would like to develop some Bokeh Extensions from React or Vue resources to Panel org. But I don't know how to get started.

#### Solution

Provide one or more examples of using Bokeh Extensions built using React or Vue.

For example some integrations with https://material-ui.com/ or https://bootstrap-vue.org/. And preferably something with bidirectional communication.

#### Additional Context

I actually prefer developing using plain typescript or via web components. But there is just a huge resource of React and Vue libraries I cannot use right now.

FYI. @philippjfr 
user: MarcSkovMadsen
issue_10794: state: open
title: [FEATURE] Page Visibility API
body: Problem:
[I wanted to utilize the Page Visibility API to stop/start an AJAX callback in a plot](https://discourse.bokeh.org/t/utilizing-page-visibility-api/6914).

There was a problem with accessing the variables for the source and other tools that were available on the page. In the end, I could access the variables in JS by naming the variables in Python and referencing them using `window.Bokeh.documents[0].get_model_by_name("foo")`

There could be an easier way to access those elements. Maybe let the `args` in `CustomJS` be available in JS in some way? Not a JS expert here so I do not know how to do it :(
user: zybex86
issue_10789: state: open
title: [FEATURE]On demand plot generation for a specific session
body: I'd like on demand plot generation based on user input. Consider the following html templat page that tries to fetch a page and expands it in a div based on a button click:
```html
<html>
<head>
   
    <script type="text/javascript">
        function openvtab(evt, tabname) {
            fetch('/tabs/' + tabname)
                .then(response => response.text())
                .then(html => {
                    console.log(document.getElementById('tabitems'));
                    document.getElementById('tabitems').innerHTML = html
                    })
        }
    </script>
</head>
<body>
    <div class="mytab" style="width:135px">
      <button class="button-active tablink" onclick="openvtab(event, 'a1')">A1</button>
      <button class="button-inactive tablink" onclick="openvtab(event, 'b1')">B1</button>
    </div>
    <div style="margin-left:140px">
      <div id="tabitems" class="vani-tab" style="display:block"></div>
    </div>
</body>
</html>
```
Now I can spawn a page via flask:

```python
p1 = figure(..., name='plot1')
p2 = figure(..., name='plot2')
curdoc().add_root(p1)
curdoc().add_root(p2)

@app.route('/')
def index():
    with pull_session(url="..") as dsession:
       session['sessionid'] = desession.id
       return render_template('index.html')
```

What I'm now looking for is an api, say session_get_plot, such that I can get a plot for an associated session:
```python
app.route('/tabs/<string:tabname>')
def renderTab(tabname):
    #tabname can be plot1 or plot2 here.
    return session_get_plot(session_id=session['session.id'], url='...', plotname=tabname)
```
For an enterprise scale application, with lots of datapoints and lots of different plots, I can't really afford to plot everything in one shot.



user: sridhar
issue_10782: state: open
title: Typo in prelude.ts
body: There is a typo in prelude.ts which becomes part of `bokeh-[VERSION].min.js`. "lenght" should be replace with "length"

https://github.com/bokeh/bokeh/blob/90e83b256dafa1371cc90d4e03ffcdc006b8e172/bokehjs/src/compiler/prelude.ts#L65
user: dvbuntu
issue_10778: state: open
title: [BUG] Value and label not synced for Select widget
body: #### Software version info
bokeh 2.1.0
chrome  86.0.4240.198 (Official Build) (64-bit)
macOS 10.14.6 Mojave

#### Description of expected behavior and the observed behavior
Context: 
* bokeh server application
* a `Select` widget with options in the form `List ( Tuple ( String , String ) )`
* a callback which changes the option labels

Expect: 
- can change options and reselect value on server, changes appear on client

Observed:
- the `Select` widget can get to a state in which the displayed label does not correspond to the current value

#### Complete, minimal, self-contained example code that reproduces the issue

File `main.py`
```
from bokeh.models.widgets import Select, Button, Div
from bokeh.layouts import column
from bokeh.io import curdoc

doc = curdoc()

select = Select(
    options=[("1", "one"), ("2", "two")],
)
select.value = "1"

report = Div(text="None")

def button_callback(event):
    hold = select.value
    select.options = [("1", "ONE"), ("2", "TWO")]
    select.value = hold
    report.text = f"Options now {select.options}, value now {select.value}"

button = Button(label="Change options")
button.on_click(button_callback)

doc.add_root(column([select, button, report]))
```

To run from the directory containing `main.py`
```
bokeh serve . --show
```

#### Stack traceback and/or browser JavaScript console output

No errors showing in developer tools or python.

#### Screenshots

1. Initial state, selecting value "1", label "one":
<img width="336" alt="Screenshot 2020-12-12 at 15 27 50" src="https://user-images.githubusercontent.com/31922007/101988188-fb5b8100-3c8f-11eb-839f-6148c74fd088.png">

2. Hit button to change options - as expected, the label changes to "ONE" the new label
<img width="338" alt="Screenshot 2020-12-12 at 15 28 01" src="https://user-images.githubusercontent.com/31922007/101988196-08787000-3c90-11eb-821c-90b11108b2d3.png">

3. Reload and select value "2"
<img width="338" alt="Screenshot 2020-12-12 at 15 28 10" src="https://user-images.githubusercontent.com/31922007/101988232-3b226880-3c90-11eb-91b4-8396978688a6.png">

4. Hit button to change options - the label changes to "ONE" on client but the value is "2" on server
<img width="338" alt="Screenshot 2020-12-12 at 15 27 42" src="https://user-images.githubusercontent.com/31922007/101988241-49708480-3c90-11eb-814a-567d85d82234.png">



user: comatrion
issue_10776: state: open
title: [FEATURE] GlyphLegendItem for arbitrary legend items
body: **Is your feature request related to a problem? Please describe.**
Annotations, colored axes, and other non-glyph elements of a plot have not been available to list in a Legend as it is written today.

**Describe the solution you'd like**
Two new LegendItem types:
- LineLegendItem: a line with visual properties set (e.g. for identifying colored axes or spans)
- FillLegendItem: A filled rect with visual properties set (e.g. for colored areas)

user: ghost
issue_10775: state: open
title: [BUG] Automatic resizing not working when zoom is used
body: ### Software Version Info
Python: 3.8.5
bokeh: 2.2.2 & 2.2.3
OS: Linux Mint 20 Cinnamon
Browser used to display generated html: Firefox 83.0 (64-bit) and Google Chrome Version 86.0.4240.183 (Official Build) (64-bit)

### Description of expected behavior and the observed behavior
In a plot with two or more VArea glyphs and `y_range` set to ` DataRange1d(only_visible=True)` ([docs](https://docs.bokeh.org/en/latest/docs/reference/models/ranges.html?highlight=datarange1d#bokeh.models.ranges.DataRange1d.only_visible)) the behavior when activating/deactivating one of the VArea glyphs is inconsistent. When zoom of plot is unmodified, everything works as expected, i.e. when one VArea is hidden the plot boundaries are automatically adjusted to only show the remaining VArea. When the zoom is used, the plot boundaries are no longer automatically updated when one of the VArea glyph is activated/deactivated. 

Expected behavior: Plot boundaries are always recomputed and plot is adjusted after activating/deactivating plotted glyphs (assuming ` DataRange1d(only_visible=True)` is used). 

In an example with multiple connected figures in a grid of plots, I observed that the automatic resizing with using zoom works in some cases (one case = one reload of html in browser). However, I was not able to get automatic resizing working with using zoom with the minimal example below. 

### Minimal (not) working example

```
from bokeh.models import ColumnDataSource, Legend, LegendItem
from bokeh.models.ranges import DataRange1d
from bokeh.models.glyphs import VArea, Line
from bokeh.colors.named import orange
from bokeh.plotting import figure
from bokeh.io import show
import numpy as np


x = np.asarray([1, 1, 2, 2, np.nan, 3, 3, 4, 4])
v = np.asarray([0, 1, 1, 0, np.nan, 0, 1, 1, 0])

p = figure(
    y_range=DataRange1d(only_visible=True),
    tools=['xpan', 'xwheel_zoom', 'xbox_zoom', 'hover', 'reset'],
    sizing_mode='stretch_both',
)
length = 2
color = orange
legends = []
for i in range(length):
    source = ColumnDataSource(dict(x=x+i, y1=np.zeros_like(v)+length-i, y2=v+length-i))
    vareaGlyph = VArea(x="x", y1="y1", y2="y2", fill_color=color)
    varea = p.add_glyph(source, vareaGlyph)
    legends.append(LegendItem(label='S{}'.format(i), renderers=[p.renderers[i]]))

legend = Legend(items=legends, location='top_right', click_policy="hide")
p.add_layout(legend)
    
show(p)
```

user: cirnod
issue_10765: state: open
title: [FEATURE] Allowing native Bokeh server app support on Visual Studio Code
body: ### My problem / request

Hi, 

(This post is related to what [I thought was a bug in the Jupyter extension of Visual Studio Code](https://github.com/microsoft/vscode-jupyter/issues/4132) and is actually more of a missing feature in Bokeh)

I am working with Bokeh and Jupyter Notebooks in Visual Studio Code because, well ... I like them. After a bit of fiddling with Server Apps for animated plots (following [this example](https://github.com/bokeh/bokeh/blob/branch-2.3/examples/howto/server_embed/notebook_embed.ipynb)), I realised that they show no output in VSCode, and I did not see any error message while running the code.

### Alternatives considered

I have run the code successfully in Jupyter Lab & Jupyter Notebook. So it is a VSCode+Bokeh problem.

### What seems to be missing 

Some people of the Jupyter Notebook community (thanks, @rchiodo !) have pointed me to the following error happening during the code execution (somehow I could not see it):

```
ERROR:bokeh.server.views.ws:Refusing websocket connection from Origin 'vscode-webview://8676e056-ff6e-461f-b0b9-4b600c50665f';                       use --allow-websocket-origin=8676e056-ff6e-461f-b0b9-4b600c50665f or set BOKEH_ALLOW_WS_ORIGIN=8676e056-ff6e-461f-b0b9-4b600c50665f to permit this; currently we allow origins {'localhost:8888'}
WARNING:tornado.access:403 GET /ws?platform=electron (::1) 1.00ms
```

### Solving the issue

Is there any way we could :

1. Allow the plots from VSCode on-the-fly using just show() ?
2. Alternatively, provide a bit more info to the user on where to set these options either :
   * as a warning / message when the code is run, 
   * as a additional comment in the last lines of [the nice example from above](https://github.com/bokeh/bokeh/blob/branch-2.3/examples/howto/server_embed/notebook_embed.ipynb),
   * or even in the documentation ?

Cheers,
user: bsaintmichel
issue_10758: state: open
title: New DataTable Feature Request: Option to make all selected rows in a DataTable non editable (until they are deselected)
body: Hello,

My DataTable is connected to a graph and the user is allowed to make changes to the DataTable rows. And in order to plot the changes made to the data table, the user is required to ‘select’ the table row by ticking the checkbox in the index of the table.
(Please find a reference photo in the link at the end of the message)

Now, I want to disallow the user to make any changes to the row that’s selected. Is that feature that can be included in the future version? 


Following is the thread of my question on Bokeh Discourse that led to this feature request:
https://discourse.bokeh.org/t/datatable-make-all-selected-rows-in-a-datatable-non-editable-until-they-are-deselected/6845/3
user: garvk
issue_10751: state: open
title: [FEATURE] New Indicator Marker (or annotation?)
body: ref: https://discourse.bokeh.org/t/refactoring-custom-markerview-from-v2/6156

<img width="148" alt="Screen Shot 2021-01-19 at 9 51 25 AM" src="https://user-images.githubusercontent.com/1078448/105073438-ee267480-5a3b-11eb-85e1-108660e930a2.png">


The current triangle marker is face-centered, but a the linked discourse post brings up a valid and not-uncommon need for a point-centered triangle that can be used as an "indicator". This could be added very as a new "indicator" marker, or it could be added as some sort of new annotation. 

This could be accomplished with a single `Indicator` glyph that can be rotated around the point that is "pointing" at the position coordinate.

This can't be accomplished currently with bare `ArrowHead` annotations, because their configuration is controlled by an owning `Arrow` ~~(also they can't be vectorized)~~.
user: bryevdv
issue_10740: state: open
title: Allow arrow heads to inherit visuals from arrow annotation
body: When creating a test in PR #10739, I had to set `line_width` twice, once in `Arrow` and the other in `OpenHead` arrow head. It would be useful to have arrow head to inherit visuals by default, but still allow them to be overridden. I would introduce `"inherit"` keyword (similar to `"auto"`) that would indicate inheritance.
user: mattpap
issue_10706: state: open
title: [BUG] DataCube not respecting 'target' arg on initial creation
body: ref: https://discourse.bokeh.org/t/datacubes-target-and-collapsed-parameters/6759/2

The poster on this Discourse thread describes and illustrates a situation where the intent is to have the DataCube _fully expanded_ upon initial drawing, which it does not seem to want to do. I've been able to replicate this.

The OP is copying a printout of the contents of the 'row_indices' and 'labels' arrays of the `target` attr when the table is fully expanded, and then trying to pass that in upon initial creation to replicate the same display of the DataCube, but the DataCube still initially appears collapsed. 

I altered the script a little bit to have it immediately print out `target.data` after creating the DataTable. The arrays are as they should be, the same as were passed in, but what actually appears in the browser does not reflect them.

It seems like one of two things is happening here:
1) DataTable is ignoring the `target` arg upon creation
2) There needs to be some clarification about how to properly set `target`. This is entirely possible, because documentation on DataCube is very light

I've just copied the code example from Discourse below:

```
from bokeh.io import output_file, show, curdoc
from bokeh.models import Button, DataCube, ColumnDataSource, StringFormatter, TableColumn, GroupingInfo, SumAggregator
from bokeh.layouts import row

source = ColumnDataSource(data=dict(
    d0=['A', 'E', 'E', 'E', 'J', 'J', 'M'],
    d1=['B', 'C', 'D', 'H', 'K', 'L', 'N'],
    px=[10,  20,  30,  40,  50,  60,  70 ],
))

#target = ColumnDataSource(data=dict(row_indices=[], labels=[])) # initial
target = ColumnDataSource(data=dict(row_indices=[[0], 0, [1, 2, 3], 1, 2, 3, [4, 5], 4, 5, [6], 6], labels=['A', 'B', 'E', 'C', 'D', 'H', 'J', 'K', 'L', 'M', 'N'])) # after finding out this was what target was equal to after expanding all rows of the datacube

formatter = StringFormatter(font_style='bold')

columns = [
    TableColumn(field='d1', title='Name', width=40, sortable=False, formatter=formatter),
    TableColumn(field='px', title='Price', width=40, sortable=False),
]

grouping = [
    GroupingInfo(getter='d0'),
]

def callback():
    print(cube.target.data)

cube = DataCube(source=source, columns=columns, grouping=grouping, target=target)
print(cube.target.data)

button = Button()
button.on_click(callback)

curdoc().add_root(row(button, cube))


```


user: ghost
issue_10694: state: open
title: Clarify process for working on BokehJS locally
body: **Is your feature request related to a problem? Please describe.**
When developing locally, including changes to BokehJS, the env var `BOKEH_RESOURCES` needs to be set to `inline` for the local js to be used and changes to show up. 

This env var can be seen in an example in the Developer Guide under ["Next Steps,"](http://docs.bokeh.org/en/latest/docs/dev_guide/setup.html#next-steps) but I am proposing moving it up, explaining what it does, and making it clear that it's necessary when making JS changes.

I got bitten by this this weekend. :) I also heard it came up in the most recent sprints, but I don't know much more about that (cc @pavithraes ?).

**Describe the solution you'd like**
A description of the purpose and effect of `BOKEH_RESOURCES=inline` probably under the ["Building and Installing"](http://docs.bokeh.org/en/latest/docs/dev_guide/setup.html#id11) section of the Developer Guide (at least).

**Describe alternatives you've considered**
n/a

**Additional context**
cc @tcmetzger @bryevdv

user: ghost
issue_10689: state: open
title: [BUG] column and row destroy datatable column width property in 2.2.3
body: Running on WIN10, python 3.6.2, bokeh 2.2.3, Chrome, Jupyter notebook

Currently moving from 1.3.4 to 2.2.3. Encountered this bug, where the column width settings are lost when the datatable is instered in a row or column layout. To illustrate the behavior:


```
from bokeh.io import show, output_notebook
from bokeh.layouts import column, row
from bokeh.models import ColumnDataSource, TableColumn, DataTable
import pandas as pd
output_notebook()
cWidths = [200,20,20,20,400]
columns = [TableColumn(field=i,width=cWidths[j], title=i ) for j,i in enumerate(['strategy','pos','order','data','status'])]  #if i != 'colors' else HTMLTemplateFormatter(template=colorCol))
status_src = ColumnDataSource(pd.DataFrame(columns=['strategy','pos','order','data','status']))

statusTable = DataTable(source=status_src, columns=columns, width=720, editable=False,index_position=None)

show(statusTable) # column widths are as expected
show(column(statusTable)) # column widths are lost
show(row(statusTable)) # column widths are lost

```


user: EdTheEagle
issue_10661: state: open
title: [FEATURE] server_document(session_id=...)
body: When only accepting signed sessions, I would have to use `server_session` as `server_document` has no `session_id` parameter. I also need to pass some arguments which `server_session` cannot do (as passing args only makes sense when creating the session), so I need to wrap it in `pull_session`, but this unnecessarily pulls in the entire document.

My current workaround mimics `server_session` by adding the right header:

```python
from bokeh.embed import server_document
from bokeh.util.token import generate_session_id

def embed_bokeh(url, **kwargs):
    session_id = generate_session_id(signed=True)
    headers = {'Bokeh-Session-Id': session_id}
    return server_document(url=url, arguments=kwargs, headers=headers)
```

Please add a `session_id` parameter to `server_document` like the one of `server_session`.
user: timdiels
issue_10659: state: open
title: [BUG] Merged tool of subfigures is not marked as active
body: #### software version info (bokeh, python)

Python: 3.6.10
Bokeh: 2.2.3

#### Description of expected behavior and the observed behavior

To activate a tool other then by default bokeh gives the option of setting parameters like ```active_scroll```. If there is only one figure, it works perfect and the tool is marked as active. But if the plot is a gridplot or some other layout, the merged tool is not working as expected.

#### minimal example

```
from bokeh.plotting import figure, show, output_notebook
from bokeh.layouts import gridplot
output_notebook()

p1 = figure(title='figure #1', active_scroll ="wheel_zoom", plot_width=600, plot_height=200)
p1.line([1,2,3,4,5], [2,5,8,2,7], color="red")
show(p1)
```
This is the correct output:
![correct](https://user-images.githubusercontent.com/68053396/98731617-4555fc00-239e-11eb-8135-92dafd8c94d6.PNG)

If the code is extended by another figure, it is not working anymore.

```
p2 = figure(title='figure #2', x_range=p1.x_range)
p2.line([1,2,3,4,5], [4,3,4,1,3], color="blue")
show(gridplot([p1,p2], ncols=1, plot_width=600, plot_height=200, merge_tools=True))
```
![incorrect](https://user-images.githubusercontent.com/68053396/98731639-4f77fa80-239e-11eb-89f6-0e196bfc19e5.PNG)

The Zoom is working in my jupyter notebook without a click on the symbol, but the WheelZoomTool is not marked as active as you would expect. In fact the first click on the symbol will activate it again.

user: mosc9575
issue_10658: state: open
title: [FEATURE] Target ES5/ES6 with BokehJS
body: **Is your feature request related to a problem? Please describe.**
Yes, I am unable to use BokehJS v2.2.3 in legacy webpack projects as BokehJS makes use of the ES2020 syntax `export ns from` syntax in the exported file `build/js/lib/index.js` which is not widely supported. See below:
```js
export * as embed from "./embed";
export * as protocol from "./protocol";
export * as _testing from "./testing";
```
Previous BokehJS version 2.0.2 did not have this issue.

**Describe the solution you'd like**
Target a lower ECMAScript language spec when publishing BokehJS, i.e. ES5/ES6.

**Describe alternatives you've considered**
I've considered the following:
* updating our legacy projects webpack/babel configs but this has negative impacts on other packages and tooling used in our project.
* remaining on old version 2.0.2 but our projects requires the use of bug fixes in v2.2.3.

Currently we are attempting to work around this by copying bokehjs directory out of node_modules and modifying the syntax in `build/js/lib/index.js` to use the following:
```js
import * as embed from './embed';
import * as protocol from './protocol';
import * as _testing from './testing';

export { version } from './version';
export { index } from './embed';
export { embed };
export { protocol };
export { _testing };
export { logger, set_log_level } from './core/logging';
export { settings } from './core/settings';
export { Models } from './base';
export { documents } from './document';
export { safely } from './safely';
// # sourceMappingURL=index.js.map
```
Obviously copying BokehJS out of node_modules this is less than ideal.

**Additional context**
Targeting a more widely used ECMAScript standard will allow for more users to utilize BokehJS and should be easily achievable.
user: joshuadavidson
issue_10657: state: open
title: better messaging on invalid plot sizes
body: **Is your feature request related to a problem? Please describe.**
If a plot size is chosen that cannot accommodate all plot elements, the error messaging is just a generic "invalid bbox" message in the javascript console.

**Describe the solution you'd like**
I think the messaging here could be improved to make it immediately obvious to the user what needs to be fixed.

**Describe alternatives you've considered**
n/a

**Additional context**
Example that illustrates the error:
```
from bokeh.plotting import figure, show
from bokeh.models import DatetimeTickFormatter
from datetime import datetime

# choosing dates for the x-axis, to result in a wide label requirement, greater than the height assigned
x_str = ['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-04',
         '2020-01-05', '2020-01-06', '2020-01-07', '2020-01-08',
         '2020-01-09']
x = [datetime.strptime(i, '%Y-%m-%d') for i in x_str]
y = [5, 10, 7, 2, 4, 11, 6, 8, 7]


p = figure(width=700, height=50, x_axis_type='datetime')
p.line(x=x, y=y)
p.xaxis.formatter = DatetimeTickFormatter(hours=['%Y-%m-%d'],
                                    days=['%Y-%m-%d'],
                                    months=['%Y-%m-%d'],
                                    years=['%Y-%m-%d'])
# p.xaxis.major_label_orientation = "vertical"

show(p)
```

As written, it produces a functional (despite a squished y-axis) plot. Uncommenting the `p.xaxis.major_label_orientation` line, however, produces a blank page and this message in the JS console:
`Uncaught Error: invalid bbox {left: 24, top: 5, right: 670, bottom: -23}`

user: ghost
issue_10656: state: open
title: [FEATURE] Predefined breaks for LinearColorMapper
body: I'm developing an application to analyze arbovirus infections in the neighborhoods of two cities. Heat maps are generated for each city, their color scheme varies according to the count of infections in the neighborhoods. One of the problems in generating color of these maps is the interest data distribution. The vector of infection counts for each city has very distant values, such as the vector `v1 = [1, 2, 3, 3, 4, 7, 8, 9, 15, 20, 39, 40]`. The `LinearColorMapper` with 4 colors would generate mapping intervals with size 10 breaks, such as `[1, 10), ..., [30, 40]`. So, as we can see, more than half of `v1` would be included in the break `[1, 10)`, causing many neighborhoods to be mapped to a single color. An ideal set of intervals for this problem would be `c = {[1, 4], [7, 9], [15, 20], [39, 40]}`, as it would allow for more color abundance on maps. An alternative to solve this problem would be allow the `LinearColorMapper` function to receive externally generated intervals or to add a resource in the `LinearColorMapper` function that allows selecting the algorithm for generating breaks, such as Fisher-Jenks and Jenks-Caspall.
user: lrdsouza
issue_10654: state: open
title: [FEATURE] Throttle for the RangeTool for Python callbacks
body: 
This feature request is the follow-up of a question asked on Discourse ([here](https://discourse.bokeh.org/t/throttle-or-callback-policy-for-the-rangetool/6682)).
Building on the example I found on the Bokeh doc showing the use of Range_Tool ([here](https://docs.bokeh.org/en/latest/docs/gallery/range_tool.html)), I made a dashboard with a RangeTool and a python callback function on change of the Range Tool. The issue is that my call-back function takes 0.5s to run. So this induce a big lag when panning/zooming. A possible solution will be to have the callback only on mouse release and not at every step on the panning.

I found this issue reported several times for Slider. A solution there is to throttle the values (reducing the refresh rate) with `slider.on_change('value_throttled', update_table)`

Would it be possible to have a similar behavior with `RangeTool` ?
Something like `range_tool.x_range.on_change('start', update_table)`

A minimal working example can be found on the discourse topic [here](https://discourse.bokeh.org/t/throttle-or-callback-policy-for-the-rangetool/6682).

Thanks a lot for this great tool ! 
user: facero
labels: tag: tools: Related to the toolbar and individual tools
issue_10631: state: open
title: Make json_items ingestible by python API
body: I want Bokeh to provide some file format so that I can write to it and load it back in Python and do some surgery. Even if I cannot interact with it like the existent HTML format. 

Thanks!
user: HuangYanqiColumbia
issue_10582: state: open
title: [FEATURE] Heatmap limit visible rows
body: **Is your feature request related to a problem?**
I was trying to create a heatmap from a dataframe which contains around 10 columns and 300 rows.
I followed [this example](https://docs.bokeh.org/en/latest/docs/gallery/unemployment.html) and I will be using it to further explain my question.
My goal is to limit the user to only see a restricted amount of rows but, at the same time, still be able to navigate through the whole data (with the pan tool for example).
In the provided example it would be refected as being able to only see from January to April (4 rows), February to May, March to June, etc…

**Describe the solution you'd like**
From what I understood from @p-himik [here](https://discourse.bokeh.org/t/heatmap-limit-visible-rows/6518), having the [start](https://docs.bokeh.org/en/latest/docs/reference/models/ranges.html#bokeh.models.ranges.FactorRange.start) and [end](https://docs.bokeh.org/en/latest/docs/reference/models/ranges.html#bokeh.models.ranges.FactorRange.end) of a FactorRange as writable would be a good solution to my problem.

**Describe alternatives you've considered**
I'm not familiar enough with bokeh to provide an alternative solution but I'm open to any other strategy as long as it solves my problem.

**Additional context**
Image 1 shows heatmap with all the data:

<p align="center">
  <img src="https://user-images.githubusercontent.com/19196034/96248500-917e6e00-0fa3-11eb-9ceb-2e11c8583f07.png">
</p>

Image 2 shows how I want the heatmap to look like (this was achieved by restricting the y-range to x rows) but at the same time it should allow the user to keep panning to see the rest of the data which it does not:
<p align="center">
  <img src="https://user-images.githubusercontent.com/19196034/96248528-993e1280-0fa3-11eb-8fff-750983d248ec.png">
</p>

Potentially similar to #9254
user: afonsobspinto
issue_10570: state: open
title: [BUG] Mutual exclusivity of polygon-select box-select, and others.
body: It is possible to have multiple active tools that have mutually-exclusive functionality. For instance, it is possible to have `box-select`,and `polygon-select`/`tap` tools activated simultaneously. 
I can see why you would might want to have `tap` and `box-select`/`box-zoom` simultaneously, but not `polygon-select` and `box-select` simultaneously. It makes point-selection very difficult.
Another problematic "couple" is `tap` and `pan`, where if you pan a dense plot while having the `tap` tool activated, then at the end of the panning, a point on which you might have started gets selected and "kills" the previous selection.
user: erezinman
issue_10567: state: open
title: [FEATURE] Download data as a CSV
body: I added a tool called `SaveDataTool` for downloading the data that's in a plot/datasource - either selected values or not.
This tool also has a formatting mechanism similar to that of the hover tool. It is, however not the same.
I also added a specialized icon for that tool.

The PR for that is [here](https://github.com/bokeh/bokeh/pull/10566).

I checked the code when it was added as a custom tool to the toolbar using the custom-tool framework (`__implementation__ = TypeScript(...)`), but I didn't manage to check it as an internal Bokeh tool, as I don't know how. I did, however follow how and where the `SaveTool` is integrated, and where it appears, and I believe it should work.

Please review my PR, and help me integrate it into the main branch. I also have a documentation compilation issue I don't know the source of, so help there would also be appreciated.

Thanks.
user: erezinman
issue_10534: state: open
title: [FEATURE] Change font style of categorical label ticks independently
body: **Is your feature request related to a problem? Please describe.**
It is not related to a problem 

**Describe the solution you'd like**
I would like to be able to customize independently the font label ticks as described here 
https://discourse.bokeh.org/t/change-independently-categorical-ticks/6434/2

I think a new separate “tick font formatter” where you can make some important items bold or a different color should be nice. 

 
**Describe alternatives you've considered**
In the meantime I am trying to implement  a custom extension Axis subclasss to override the font style. 

**Additional context**
I would like to have a figure like this 

<img width="720" alt="inyectivities_bold_light" src="https://user-images.githubusercontent.com/46707985/94487377-4862a780-01b7-11eb-8bb1-309371619863.png">

You can see the label ticks nearest to the xaxis some are bold and some not. 
user: nghenzi
issue_10512: state: open
title: [BUG] datatable column autosize_mode = fit_columns not rendering
body: #### Software version info
bokeh 2.2.1
panel 0.9.7
chrome  78.0.3904.97 (Official Build) (64-bit)
macOS 10.14.6 Mojave

#### Description of expected behavior and the observed behavior
Context: a table with multiple columns and rows and either
- `autosize_mode` default
- `autosize_mode = 'fit_columns'`

Expect: 
- to see data in all columns, both modes
- that the table takes up it's natural height rather than the default height (feature request #8585 fixed by #10353)

Observed:
- data for some columns (e.g. columns "c5", "c6") is not rendered for `autosize-mode = 'fit-columns'` 
- sorting a column or other action triggering layout causes the missing data to show
- tables are displayed with default height not natural height

#### Complete, minimal, self-contained example code that reproduces the issue
```
from bokeh.models.widgets import DataTable, TableColumn
from bokeh.models import ColumnDataSource
from bokeh.layouts import column
from bokeh.plotting import show

data = dict(
    c1=[5, 40000],
    c2=[5, 40001],
    c3=[5, 40002],
    c4=[5, 40003],
    c5=[5, 40004],
    c6=[5, 40005],
)

table1 = DataTable(
    source=ColumnDataSource(data),
    columns=[TableColumn(field=col, title=col) for col in data],
)

table2 = DataTable(
    source=ColumnDataSource(data),
    columns=[TableColumn(field=col, title=col) for col in data],
    autosize_mode="fit_columns",
)

show(column([table1, table2], background="yellow"))
```

#### Stack traceback and/or browser JavaScript console output

No errors showing in developer tools or python.

#### Screenshots

<img width="645" alt="Screenshot 2020-09-20 at 11 18 04" src="https://user-images.githubusercontent.com/31922007/93709038-fecce980-fb32-11ea-9406-548c50c56db3.png">




user: comatrion
issue_10504: state: open
title: Cannot display Icon on standalone basis
body: I'm currently trying to implement Icon functionality for Panel and would also like to give back to Bokeh. See for example https://github.com/bokeh/bokeh/issues/10502.

One thing that me is that I cannot use the icon on a standalone basis. I get an error like

```python
c:\repos\private\panel-extensions-template\.venv\lib\site-packages\bokeh\core\has_props.py in __setattr__(self, name, value)
    280 
    281             raise AttributeError("unexpected attribute '%s' to %s, %s attributes are %s" %
--> 282                 (name, self.__class__.__name__, text, nice_join(matches)))
    283 
    284     def __str__(self):

AttributeError: unexpected attribute 'align' to SVGIcon, possible attributes are fill_color, icon_name, js_event_callbacks, js_property_callbacks, name, size, spin_duration, subscribed_events, svg or tags
```

I can see that is because the `AbstractIcon` is just a `Model` and not a widget or similar.

![image](https://user-images.githubusercontent.com/42288570/93551720-e01df580-f96e-11ea-8d46-228563f74d2b.png)

But I believe it would be nice to be able to use an Icon on a standalone basis for the following use cases

- Documentation
- Exploration and Learning.
- Displaying information as symbol.

For example for documentation and learning it would be nice that a user could have the Icon on a standalone basis in a Notebook and see what happens if for example the `rotation` or `spin_duration` is changed.

Also the icon could be used to convey information as a standalone symbol. See for example 

https://fontawesome.com/how-to-use/on-the-web/styling/layering

![image](https://user-images.githubusercontent.com/42288570/93551876-4efb4e80-f96f-11ea-86da-3f0defa4c70a.png)

The might be used on buttons. But could also stand alone and convey information.

## Solution

Change `AbstractIcon` to Widget or similar.
user: MarcSkovMadsen
issue_10502: state: open
title: Add Icon widget to Bokeh
body: I'm working on wrapping the Fast Design components into Bokeh extensions and Panel widgets. A part of this needs support of Icons.

I've looked at how Icons are used and supported today. See https://github.com/holoviz/panel/issues/1586. Fast just uses inline SVG and it's also preferred by CSS tricks https://css-tricks.com/pretty-good-svg-icon-system/.

So my thinking is that this is a good a general way to support icons whether they are coming from font-awesome, material, fluid, adobe designer or any other place.

So I have built a prototype. And now the question is where the bokeh extension should live: Bokeh, Panel or Awesome-Panel-Extensions.

I would like to contribute them to Bokeh as a first choice.

**Let me know if you would accept a PR and help me review it.**

![svg_icon](https://user-images.githubusercontent.com/42288570/93434305-4439ae80-f8c8-11ea-83dc-96eb6d7889f7.gif)

#### POC Bokeh Extension

```typescript
import {AbstractIcon, AbstractIconView} from "@bokehjs/models/widgets/abstract_icon"
import * as p from "@bokehjs/core/properties"

// See https://stackoverflow.com/questions/494143/creating-a-new-dom-element-from-an-html-string-using-built-in-dom-methods-or-pro
/**
 * @param {String} HTML representing a single element
 * @return {Element}
 */
function htmlToElement(html: string) {
  var template = document.createElement('template');
  html = html.trim(); // Never return a text node of whitespace as the result
  template.innerHTML = html;
  return <HTMLElement>template.content.firstChild;
}

export class SVGIconView extends AbstractIconView {
  model: SVGIcon

  connect_signals(): void {
    super.connect_signals()
    this.connect(this.model.change, () => this.render())
  }

  render(): void {
    super.render()

    if (this.model.svg===null && this.model.svg===""){return}
    const el = htmlToElement(this.model.svg)
    this.el.innerHTML=""
    this.el.appendChild(el)
    this.el.style.display = "inline"
    this.el.style.verticalAlign = "middle"
    el.style.height = `${this.model.size}em`
    el.style.width = `${this.model.size}em`
    el.style.fill = this.model.fill_color
    if (this.model.spin_duration>0){
      // See https://codepen.io/eveness/pen/BjLaoa
      const animationDuration = `${this.model.spin_duration}ms`

      el.style.setProperty("-webkit-animation-name", "spin")
      el.style.setProperty("-webkit-animation-duration", animationDuration)
      el.style.setProperty("-webkit-animation-iteration-count", "infinite")
      el.style.setProperty("-webkit-animation-timing-function", "linear")
      el.style.setProperty("-moz-animation-name", "spin")
      el.style.setProperty("-moz-animation-duration", animationDuration)
      el.style.setProperty("-moz-animation-iteration-count", "infinite")
      el.style.setProperty("-moz-animation-timing-function", "linear")
      el.style.setProperty("-ms-animation-name", "spin")
      el.style.setProperty("-ms-animation-duration", animationDuration)
      el.style.setProperty("-ms-animation-iteration-count", "infinite")
      el.style.setProperty("-ms-animation-timing-function", "linear")
      el.style.setProperty("animation-name", "spin")
      el.style.setProperty("animation-duration", animationDuration)
      el.style.setProperty("animation-iteration-count", "infinite")
      el.style.setProperty("animation-timing-function", "linear")

    }
    el.classList.add("icon")
    if (this.model.icon_name!=null && this.model.icon_name!==""){
      el.classList.add(`icon-${this.model.icon_name}`)
    }
  }
}

export namespace SVGIcon {
  export type Attrs = p.AttrsOf<Props>

  export type Props = AbstractIcon.Props & {
    icon_name: p.Property<string>;
    svg: p.Property<string>;
    size: p.Property<number>;
    fill_color: p.Property<string>;
    spin_duration: p.Property<number>;
  }
}

export interface SVGIcon extends SVGIcon.Attrs {}

export class SVGIcon extends AbstractIcon {
  properties: SVGIcon.Props

  constructor(attrs?: Partial<SVGIcon.Attrs>) {
    super(attrs)
  }

  static __module__ = "awesome_panel_extensions.bokeh_extensions.svg_icon"

  static init_SVGIcon(): void {
    this.prototype.default_view = SVGIconView

    this.define<SVGIcon.Props>({
        icon_name: [p.String, ],
        svg: [p.String, ],
        size:      [ p.Number, 1.0 ],
        fill_color: [ p.String, "currentColor"],
        spin_duration: [ p.Number, 0.0 ],
    })
  }
}
```

```python
"""The SVGIcon can be used to add SVG based icons to buttons, menus etc."""
# See https://github.com/holoviz/panel/issues/1586 for motivation, possibilities and requirements.

from bokeh.core import properties
from bokeh.models.widgets import AbstractIcon

class SVGIcon(AbstractIcon):
    """The SVGIcon can be used to add SVG based icons to buttons, menus etc."""

    icon_name = properties.String()
    svg = properties.String()
    size = properties.Float()
    fill_color = properties.String()
    spin_duration = properties.Int()

```
user: MarcSkovMadsen
issue_10496: state: open
title: [BUG] Figures in layout: when I export these figures to PNGs, each next image is 100px wider than previous
body: Hi folks!

I’m trying to export some figures to PNGs with bokeh.io.export_png(fig, filename=fname, width=width, height=height).

And I’m getting some strange behaviour: when I call this function second time, the next image is 100px wider than I ordered by width argument. Third image is 200px wider — and so on.

Here’s a small code example:

```
import numpy as np
import bokeh.io
from bokeh.plotting import *
from bokeh.layouts import column, gridplot, layout
from bokeh.models import ColumnDataSource

if __name__ == "__main__":
    # lets' prepare some date
    N = 300
    x = np.linspace(0, 4 * np.pi, N)
    y0 = np.sin(x)
    y1 = np.cos(x)
    y2 = np.abs(np.cos(x))

    # create a column data source for the plots to share
    source = ColumnDataSource(data=dict(x=x, y0=y0, y1=y1, y2=y2))

    # create a new plot and add a renderer
    left = figure(width=600, height=400, title=None)
    left.circle('x', 'y0', source=source)

    right = figure(width=600, height=350, title=None)
    right.circle('x', 'y1', source=source)

    bottom = figure(width=600, height=400, title=None)
    bottom.circle('x', 'y2', source=source)

    # put the subplots in a gridplot
    layout = layout([[left, right], [bottom]], sizing_mode='stretch_width', height=1800)

    show(layout)  # show the plots in layout: layout looks okay...

    # And let's try to export some figures to PNGs (here we export only two images, but for real tasks we have more
    # complicated layouts, and we use bokeh server to modify plots on-the-fly and export them again and again).

    for i in range(5):
        bokeh.io.export_png(left, filename="left{}.png".format(i), width=800, height=500)
        bokeh.io.export_png(right, filename="right{}.png".format(i), width=800, height=500)

    # And what we get? After each export to PNG each next image becomes 100px wider. I don't know why = (((

    # left0.png: 800x500
    # right0.png: 900x500
    # left1.png: 1000x500
    # right1.png: 1100x500
    # left2.png: 1200x500
    # right2.png: 1300x500
    # left3.png: 1400x500
    # right3.png: 1500x500
    # left4.png: 1600x500
    # left4.png: 1700x500, it's almost twice wider than ordered!
```

If this can be fixed -- it will be really cool, because we use bokeh to have a dynamic dashboard with bokeh server and it's a really important feature for us to be capable to save the plots each 100-150 steps (about 5 minutes).

P.S. I use bokeh version 2.2.1 (installed by `pip install --force bokeh`), OS: Windows 10x64, webdriver: chrome
user: Felix-neko
issue_10490: state: open
title: [FEATURE] Make it possible for annotations to receive BokehJS events
body: Started at https://discourse.bokeh.org/t/not-possible-to-add-customjs-callbacks-to-annotations/6327

It would be nice if the `CustomJS` callback would work when a user taps on the box annotation in this example
```python
from bokeh.io import show
from bokeh.models import CustomJS, BoxAnnotation
from bokeh.plotting import figure

p = figure(plot_width=400, plot_height=250)

callback = CustomJS(code="""
console.log(cb_obj);
""")

box = BoxAnnotation(top_units="screen", bottom_units="screen", left_units="screen", right_units="screen",
    top=80, bottom=100, left=10, right=30, fill_alpha=1, fill_color='red')

box.js_on_event("tap", callback)

p.add_layout(box)
show(p)
```

Note that `BoxAnnotation` is particularly different because it implements `interactive_hit`, but it only returns `true` if some mouse cursor is set for the annotation.
Other annotations don't implement the method at all. Right now I don't see why it must be so.


user: p-himik
issue_10479: state: open
title: [FEATURE] Consider simplifying use of bokeh_server_page in tests
body: As mentioned by @bryevdv [here](https://github.com/bokeh/bokeh/pull/10476#discussion_r485218670)

> The previous tests do a further round trip update and check the value new from a data source update via a `CustomAction` (on a plot) In hindsight all the tests could probably be simplified to be more like this one.

Example of the previous tests:
https://github.com/bokeh/bokeh/blob/658f80d22fe9900a08435bb51cdf59b0900028a6/tests/integration/widgets/test_autocomplete_input.py#L349-L359

Example of alternative approach:
```python
    def test_server_strict(self, bokeh_server_page) -> None:
        """Test effect of 'strict=False' without explicit callback."""
        text_input = AutocompleteInput(css_classes=["foo"], completions = ["aAaBbb", "aAaBbB"], strict=False)

        def add_autocomplete(doc):
            # note: for some reason, bokeh_server_page requires a 'canvas' in the document
            plot = Plot()
            doc.add_root(column(text_input,plot))

        page = bokeh_server_page(add_autocomplete)

        el = page.driver.find_element_by_css_selector('.foo input')
        text = "not in completions"
        enter_text_in_element(page.driver, el, text, click=1, enter=True)

        assert text_input.value == text
```
user: ltalirz
issue_10478: state: open
title: Please make it easier to reuse existing code when extending to Fast, Material or other frameworks of widgets.
body: #### My Pain

I would like my analytics (Panel) apps to look and feel awesome. A part of that is custom branding, a consistent look, feel and maybe some animations/ transitions.

I tried for a long time to style the existing Bokeh widgets. But it really takes a long time and as I see it it's not really made for changing the look and feel. A lot of the things are just hard coded in the CSS files.

Also today a lot of front end has moved from Bootstrap to Material and other Frameworks. I would like to be able to use those for example in an Enterprise setting where the official branding is not based on Bootstrap but one of the other frameworks. 

What I've found is that Web Components like MWC or Fast are nice because they extend the basic functionality of the core HTML elements. And their styling is in the shadow dom and thus does not disturb/ get disturbed by the css and javascript of Bokeh, Panel or Jupyter. And they come with design systems. I.e. you just need to change a few css properties to change the look and feel across all widgets or change from light to dark mode.

Currently I'm working on Fast. But If I'm able to speed up the development process I would like to support Material and Maybe others like Material MWC, Smart Elements, SAP UI 5 Elements, Wired Elements etc.

A prerequisite to speed up the process is code reuse and inheritance.

After having tried to extend the Bokeh models I can see that they can almost be used in an easy way but not fully. The `render` method should be refactored a bit such that 

1. construction of the element is a subfunction that can be reused or overridden.
2. the addition of eventlisteners should be a seperate subfunction that can be reused or overridden.

## Examples

### Button

The Button is prepared for reuse

![image](https://user-images.githubusercontent.com/42288570/92555734-9c3d3900-f268-11ea-9466-303ce0bef0c3.png)

And it's easy to implement a FastButton

![image](https://user-images.githubusercontent.com/42288570/92555764-afe89f80-f268-11ea-897f-0a03a258c162.png)

### TextInput

For the FastTextInput I could not get inheritance from TextInput working because of the way the code was structured. So I had to inherit from `InputWidgetView` instead.

```typescript
export class FastTextInputView extends InputWidgetView {
    model: FastTextInput

    protected input_el: HTMLInputElement

    connect_signals(): void {
      super.connect_signals()
      this.connect(this.model.properties.name.change, () => this.input_el.name = this.model.name ?? "")
      this.connect(this.model.properties.value.change, () => {this.input_el.value = this.model.value;console.log("value")})
      this.connect(this.model.properties.value_input.change, () => {this.input_el.value = this.model.value_input;console.log("value_input")})
      this.connect(this.model.properties.disabled.change, () => this.input_el.disabled = this.model.disabled)
      this.connect(this.model.properties.placeholder.change, () => this.input_el.placeholder = this.model.placeholder)

      this.connect(this.model.properties.appearance.change, () => this.input_el_any.appearance = this.model.appearance)
      this.connect(this.model.properties.autofocus.change, () => this.input_el_any.autofocus = this.model.autofocus)
      this.connect(this.model.properties.type_of_text.change, () => this.input_el_any.type = this.model.type_of_text)
      this.connect(this.model.properties.maxlength.change, () => this.input_el_any.maxlength = this.model.maxlength)
      this.connect(this.model.properties.minlength.change, () => this.input_el_any.minlength = this.model.minlength)
      this.connect(this.model.properties.pattern.change, () => this.input_el_any.pattern = this.model.pattern)
      // Could not get size working. It raises an error
      // this.connect(this.model.properties.size.change, () => this.input_el_any.size = this.model.size)
      this.connect(this.model.properties.spellcheck.change, () => this.input_el_any.spellcheck = this.model.spellcheck)
      this.connect(this.model.properties.required.change, () => this.input_el_any.required = this.model.required)
      // Could not get readonly working as a property.
      // https://github.com/microsoft/fast/issues/3852
      this.connect(this.model.properties.readonly.change, () => this.input_el_any.setAttribute("readonly", this.model.readonly))
    }


    public get input_el_any() : any {
      return <any>this.input_el;
    }


    render(): void {
      super.render()

      const fastTextField = <any>document.createElement("fast-text-field")
      this.input_el = <HTMLInputElement>fastTextField;
      this.input_el.className="bk-fast-input"
      this.input_el.addEventListener("change", () => this.change_input())
      this.input_el.addEventListener("input",  () => this.change_input_oninput())
      this.group_el.appendChild(this.input_el)

      // For some unknown reason we need to set these properties after the above
      // Otherwise for example the value is reset to ""
      fastTextField.name = this.model.name;
      fastTextField.value = this.model.value;
      fastTextField.appearance = this.model.appearance;
      fastTextField.autofocus = this.model.autofocus;
      fastTextField.placeholder = this.model.placeholder;
      fastTextField.disabled = this.model.disabled;
      fastTextField.type = this.model.type_of_text;
      fastTextField.maxlength = this.model.maxlength;
      fastTextField.minlength = this.model.minlength;
      fastTextField.pattern = this.model.pattern;
      // Could not get size working. It raises an error.
      // fastTextField.size = this.model.size;
      fastTextField.spellcheck = this.model.spellcheck;
      fastTextField.required = this.model.required;
      fastTextField.disabled = this.model.disabled;
      fastTextField.setAttribute("readonly", this.model.readonly)
    }

    change_input(): void {
      this.model.value = this.input_el.value
      super.change_input()
    }

    change_input_oninput(): void {
      this.model.value_input = this.input_el.value
      super.change_input()
    }
  }
```

### CheckBoxGroup

For the `FastCheckboxGroupView` I could use inheritance to some extend. But calling `super.render()` would add the group twice. So I don't call that.

```python
export class FastCheckboxGroupView extends CheckboxGroupView {
  model: FastCheckboxGroup;

  render(): void {
    // Cannot call super.render() as this will add the group twice
    // super.render()

    const group = div({class: [bk_input_group, this.model.inline ? bk_inline : null]})
    this.el.innerHTML="";
    this.el.appendChild(group)

    const {active, labels} = this.model
    this._inputs = []
    for (let i = 0; i < labels.length; i++) {
      let fastCheckBox = <any>document.createElement("fast-checkbox")
      if (this.model.readonly)
        // Setting the property did not work for me. Thus I set the attribute
        fastCheckBox.setAttribute("readonly", true)
      fastCheckBox.innerHTML = labels[i]
      const checkbox = <HTMLInputElement>fastCheckBox

      checkbox.value = `${i}`
      // const checkbox = input({type: `checkbox`, value: `${i}`})
      checkbox.addEventListener("change", () => this.change_active(i))
      this._inputs.push(checkbox)

      if (this.model.disabled)
        checkbox.disabled = true

      if (includes(active, i))
        checkbox.checked = true

      // const label_el = label({}, checkbox, span({}, labels[i]))
      group.appendChild(checkbox)
    }
  }
}
```

#### Additional Context

Maybe it's not such a big deal as I get up to speed implementing this. Maybe my TypeScript skills are not well developed.

But it's just nice if it's clear what you need to implement/ override, if you can reuse a lot and don't have to implement much :-)



user: MarcSkovMadsen
issue_10464: state: open
title: Support for Arrow as a ColumnDataSource
body: # Challenge

I believe Arrow is the core format for tabular format of the future.

It is starting to be the core of various dataframes like cuDf https://github.com/rapidsai/cudf. Its also at the core of for example awesome Perspective https://perspective.finos.org/.

I envision a future where no matter if you are working with pandas Dataframes or other Dataframes you are using Arrow format.

If you want to use Plotting libraries like matplotlib or Bokeh, Plot, Echarts etc you are providing the data directly or indirectly via Arrow.

For example right now I have implemented the PerspectiveViewer widget for Panel as a Bokeh Extension. It would easier and more efficient if I could just transfer and load via Arrow format instead of using ColumnDataSource and have to transform the CDS dictionary to a records based format for Perspective.

# Solution

Support storing and transfering data in Arrow. I don't know exactly what this means for Bokeh. But I would like use cases like the PerspectiveViewer above supported.

# Additional Context

@philippjfr asked me to add this FR.
user: MarcSkovMadsen
issue_10444: state: open
title: Vertical sliders' appearance is broken
body: Layout allocates space for sliders, but possibly CSS is broken, e.g.:
![image](https://user-images.githubusercontent.com/27475/91636120-672a1e80-e9fe-11ea-83b7-d6d4e161e1bb.png)


user: mattpap
issue_10442: state: open
title: bokehjs needs an idiomatic embedding API
body: Here is a try to get a feature where the horizontal axis can be shared (linked) from different plots. Plots are created on demand, not in one call.

Actually with 2.2.0 release it not possible to re-use a model (here a Bokeh.DataRange1d) and you get an error
`Uncaught (in promise) Error: models must be owned by only a single document`

But commenting in bokeh.js, the line 779 in the add_root function of the Document class allows to create different plots with a same shared horizontal axis and then having linked plots.

<pre>
add_root(model, setter_id) {
            logging_1.logger.debug(`Adding root: ${model}`);
            if (array_1.includes(this._roots, model))
                return;
            this._push_all_models_freeze();
            try {
                this._roots.push(model);
            }
            finally {
                //this._pop_all_models_freeze();                 // to allow reuse of a model 
            }
            this._trigger_on_change(new events_1.RootAddedEvent(this, model, setter_id));
        }
</pre>

I have attached a test file to expose this feature I was looking for for a while.
Ideally a way to link or unlink axis once plots are instantiated would be nice.

Here is the test html file and modifed bokeh.js and bokeh-api.js 
[linkedAxis.zip](https://github.com/bokeh/bokeh/files/5142520/linkedAxis.zip)


![linkedAxis](https://user-images.githubusercontent.com/5402758/91577417-f248dd00-e948-11ea-893c-395b61bb1a11.gif)

And of course, I would like  to have an advice to see how this commented line may break things.


user: PBrockmann
issue_10440: state: open
title: [BUG] WheelZoomTool scrolling issues with bounds
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
```
bokeh=2.1.1
python=3.6.8
jupyterlab=1.2.3
```
(Using Chrome & Windows, but don't think the browser / OS are relevant for this issue.)

#### Description of expected behavior and the observed behavior
(1) `xwheel_zoom` will fail to work if any axis (e.g., a y axis) has reached its bounds, and vice versa. This is likely due to the way in which a [zoom update is rejected if any of the ranges are at or past their bounds in `_update_ranges_individually()`](https://github.com/bokeh/bokeh/blob/5045aca77623712b52918f6a636ce55d231445e9/bokehjs/src/lib/models/plots/plot_canvas.ts#L615), although this is just based on looking at the code. This is relatively minor as the bounds could be adjusted slightly to not be reached, but its a bit counterintuitive.

(2) This is potentially a new feature request, so I can open a new ticket for this if it seems sensible. Currently, to prevent zooming/panning for one axis, say the y-axis, one can use a combination of setting `bounds=/min_interval=/max_interval=` in their `Range1d()`. However, this also constrains all of the other y-axes from being able to be zoomed/panned. It would be nice if it were possible to independently zoom/pan in one y-axis while keeping another y-axis fixed, e.g. the intent behind the [`fixedrange=True` option in `plot.ly`](https://plotly.com/python/axes/#disabling-panzoom-on-axes-fixed-range). [Maybe this would break some currently held invariants and be too much work though.](https://stackoverflow.com/a/57047479)

#### Complete, minimal, self-contained example code that reproduces the issue

Example modified from [a related stackoverflow post](https://stackoverflow.com/questions/34085548/bokeh-two-y-axis-disable-one-axis-for-zoom-panning):
```python
from bokeh.plotting import figure
from bokeh.models import LinearAxis, Range1d

s1 = figure(width=250, plot_height=250, title=None, tools="pan, wheel_zoom, xwheel_zoom, ywheel_zoom, reset")
s1.line([1, 2, 3], [300, 300, 400], color="navy", alpha=0.5)
s1.extra_y_ranges = {"foo": Range1d(start=1, end=9, bounds=(0.9, 9.1), min_interval=8)}  # bounds set here on secondary y-axis
s1.add_layout(LinearAxis(y_range_name="foo"), 'right')
s1.line([1, 2, 3], [4, 4, 1], color="firebrick", alpha=0.5, y_range_name="foo")

show(s1)
```
For (1), this example runs correctly: upon activating the `xwheel_zoom` tool and scrolling, zooming works fine. However, if either of the bounds are tightened, e.g. changing the y range to have `bounds=(1, 9.1)`, then the scrolling & zooming no longer work, even though nothing changed for the x-axis.

For (2), even though the primary y-axis is not constrained by any bounds, scrolling & zooming are not possible with `wheel_zoom` or `ywheel_zoom`.

#### Stack traceback and/or browser JavaScript console output

#### Screenshots or screencasts of the bug in action

user: azjps
issue_10439: state: open
title: Activate wheel zoom by default with BokehJS
body: With BokehJS, I would like to have the wheel zoom for scrolling activated by default.
It is not the case by default. 

An option like: 
`active_scroll = "wheel_zoom" `
is not implemented.

But changing the line 30073 of bokeh.js file from
`return et == 'tap' || et == 'pan';`
to
`return et == 'tap' || et == 'pan' || et == 'scroll';`
activates the wheel zoom.

Am I breaking something doing this ? if not could it be added in next release.

Simple code to test this is availaible from https://jsfiddle.net/PBrockmann/ct6okdya/ 

user: PBrockmann
issue_10427: state: open
title: [BUG] RangeTool handles do not work on Win10 Chrome
body: Run `examples/app/dash` Range bar is draggable but handles at the egdes are not responsive

Handles work in FF and Edge

EDIT: `range_tool.py` works tho, it is just the dash example that has a problem
user: bryevdv
issue_10425: state: open
title: Please give FileInput a more modern look and feel
body: #### My Pain

I would like to be able to create awesome analytics apps. One fundamental component for users work flows is the FileInput.

The look and feel of the current FileInput widget is basic.

![image](https://user-images.githubusercontent.com/42288570/90971812-c0ccad80-e513-11ea-930a-965a055e5eeb.png)

I would like it to look and feel more modern. One thing that is not obvious to me from the look is that the FileInput actually supports drag and drop of files. Leading to a very cumbersome work flow for users not realizing this.

![fileinputdraganddrop](https://user-images.githubusercontent.com/42288570/90971849-f70a2d00-e513-11ea-8328-17820f5031d3.gif)

Some of the alternative frameworks provide

![image](https://user-images.githubusercontent.com/42288570/90971860-186b1900-e514-11ea-856a-40c541e736dc.png)

![image](https://user-images.githubusercontent.com/42288570/90971869-33d62400-e514-11ea-92a8-c8527cf68cd4.png)

As far as I can see this could be achieved in different ways

- Leave the current FileInput as is. But document that it supports drag and drop and showcase a styled example.
- Leave the current FileInput as is. But adding some CSS and maybe external js. See https://tympanus.net/codrops/2015/09/15/styling-customizing-file-inputs-smart-way/
- Implement new or Change existing FileInput without using external js library. See for example https://www.smashingmagazine.com/2018/01/drag-drop-file-uploader-vanilla-js/
- Implement new or Change existing FileInput by using external js library. See for example [dropzone.js](https://www.dropzonejs.com/)




user: MarcSkovMadsen
issue_10421: state: open
title: [BUG] Slickgrid types not included in bokehjs
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
Bokeh 2.1.1 installed via `pip3 install bokeh`

#### Description of expected behavior and the observed behavior

It's not possible to compile TS code that references `models/widgets/tables/data_table` because the Slickgrid typescript external is missing/not installed.

#### Complete, minimal, self-contained example code that reproduces the issue

```python
from bokeh.embed import file_html
from bokeh.resources import INLINE
from bokeh.models import ColumnDataSource, DataTable
from bokeh.util.compiler import TypeScript
from bokeh.util.browser import view
from bokeh.document import Document
from bokeh.core.properties import Override

class SortedDataTable(DataTable):
    __implementation__ = TypeScript("""
import {DataTable, DataTableView} from "models/widgets/tables/data_table"
import * as p from "core/properties"

export class SortedDataTableView extends DataTableView {
  model: SortedDataTable
}
export namespace SortedDataTable {
  export type Attrs = p.AttrsOf<Props>
  export type Props = DataTable.Props
}
export interface SortedDataTable extends SortedDataTable.Attrs {
}
export class SortedDataTable extends DataTable {
  properties: SortedDataTable.Props
  __view_type__: SortedDataTableView
  static init_SortedDataTable(): void {
    this.prototype.default_view = SortedDataTableView
    this.override({
      sortable: true,
    })
  }
}
""")

    sortable = Override(default=True)

doc = Document()
doc.add_root(SortedDataTable())

if __name__ == "__main__":
    doc.validate()
    filename = "custom.html"
    with open(filename, "w") as f:
        f.write(file_html(doc, INLINE, "Demonstration of user-defined models"))
    print("Wrote %s" % filename)
    view(filename)

```

#### Stack traceback and/or browser JavaScript console output
Here's the compiler output from the example above:
```
Compilation failed:

../../opt/local/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/bokeh/server/static/js/types/models/widgets/tables/data_table.d.ts:1:23 - error TS2688: Cannot find type definition file for 'slickgrid'.

1 /// <reference types="slickgrid" />
                        ~~~~~~~~~
../../opt/local/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/bokeh/server/static/js/types/models/widgets/tables/data_table.d.ts:2:49 - error TS2307: Cannot find module '@bokeh/slickgrid'.

2 import { Grid as SlickGrid, DataProvider } from "@bokeh/slickgrid";
                                                  ~~~~~~~~~~~~~~~~~~
../../opt/local/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/bokeh/server/static/js/types/models/widgets/tables/table_column.d.ts:1:23 - error TS2688: Cannot find type definition file for 'slickgrid'.

1 /// <reference types="slickgrid" />
                        ~~~~~~~~~
../../opt/local/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/bokeh/server/static/js/types/models/widgets/tables/table_column.d.ts:5:24 - error TS2307: Cannot find module '@bokeh/slickgrid'.

5 import { Column } from "@bokeh/slickgrid";
```
user: quentinmit
issue_10419: state: open
title: [FEATURE] Python exception handling
body: When an exception happens during a python callback execution, the callback is terminated and no changes are applied to the model. The user who doesn't have access to the stdout cannot see the exception and may get confused whether the response is slow or something went wrong.
It would be great if it was possible to handle exceptions that are happening on a document level, e.g. to display a popup message to the user.

I am suggesting to introduce a Document exception_callback. Something like this:
```python
document = curdoc()

error_div = Div(text="", visible=False)
document.add_root(error_div)

def exception_handler(error, traceback):
    error_div.text = str(error)
    error_div.visible = True

document.on_exception(exception_handler)
```

In my project (using Bokeh v.2.1.1) I have achieved this by monkey patching `@_needs_document_lock` decorator. I have added an `except` section in the `try` block:
```python
                try:
                    result = func(self, *args, **kwargs)
                    if inspect.isawaitable(result):
                        # Note that this must not be outside of the critical section.
                        # Otherwise, the async callback will be ran without document locking.
                        result = await result

                # -------- PATCH START ----------
                except Exception:
                    if hasattr(self._document, "exception_callback"):
                        exc_info = sys.exc_info()

                        def _on_exception():
                            self._document.exception_callback(exc_info[1], exc_info[2])

                        self._document.add_next_tick_callback(_on_exception)
                    raise
                # -------- PATCH FINISH ----------

                finally:
                    ...
```

user: vinokurov
issue_10417: state: open
title: BEP 8: Privacy Policy 
body: This was already on my short-list todo, but NF is pushing projects to communicate their privacy policies. I have opened a WIP wiki page for BEP 8 here:

https://github.com/bokeh/bokeh/wiki/BEP-8:-Privacy-Policy

NF has a template for the policy itself, as well as a blurb that should go on all our sites here: https://github.com/numfocus/privacy-policy

cc @bokeh/core 

(Currently just getting the issues and docs in place, I will add my actual thoughts on this soon)
user: bryevdv
issue_10405: state: open
title: [BUG] ColorPicker does not respect initial color
body: ```
from bokeh.io import show
from bokeh.layouts import column, row
from bokeh.models import ColorPicker
from bokeh.plotting import Figure

plot = Figure(x_range=(0, 1), y_range=(0, 1), plot_width=350, plot_height=350)
line = plot.line(x=(0,1), y=(0,1), color="red", line_width=4)

picker = ColorPicker(title="Line Color", color="red")
picker.js_link('color', line.glyph, 'line_color')

show(column(plot, picker))
```

Picker initially displays as black. Probably a missing configuration in the widget init. 
user: bryevdv
issue_10401: state: open
title: Document width_policy, heigh_policy
body: #4407 was closed in the large layout PR work, but some of the features such as `width_policy` and `height_policy` never got documented fully, or examples added.
user: bryevdv
issue_10398: state: open
title: [BUG] Control the height of a Label object added with add_layout?
body: How can I control the height/width of a Label annotation added below/above a figure?

The use case is trying to add annotation text outside the bounds of the axes for attribution of credit for data etc. It appears this is only possible by using Label and add_layout(Label(), 'below') because if you don't include the 'below', anything outside the axes will get clipped and be invisible. (It's also possible by using render_mode='css' but then it doesn't show up when you save the figure which defeats a lot of the purpose).

Here's a minimum working example where I achieve what I want, except that for every Label you add, you add extra white space under the figure, and you compress the height of the actual plot. The tons of extra white space is highlighted when I try to add a button or widget underneath the figure for the user to do things but there ends up looking like a huge gap.

```
from bokeh.plotting import figure, output_file, show
from bokeh.models import Label
from bokeh.models.widgets import Button
from bokeh.layouts import column

# prepare some data
x = [1, 2, 3, 4, 5]
y = [6, 7, 2, 4, 5]

# output to static HTML file
output_file("lines.html")

# create a new plot with a title and axis labels
p = figure(title="simple line example", x_axis_label='x', y_axis_label='y')

# add a line renderer with legend and line thickness
p.line(x, y, legend_label="Temp.", line_width=2)


# create the four lines of credit text in the two bottom corners
label_opts1 = dict(
    x=-35, y=42,
    x_units='screen', y_units='screen'
)

label_opts2 = dict(
    x=-35, y=47,
    x_units='screen', y_units='screen'
)

label_opts3 = dict(
    x=612, y=79,
    x_units='screen', y_units='screen', text_align='right',
    text_font_size='9pt'
)

label_opts4 = dict(
    x=612, y=83,
    x_units='screen', y_units='screen', text_align='right',
    text_font_size='9pt'
)

msg1 = 'Credit 1'
msg2 = 'Credit 2'
msg3 = 'Credit 3'
msg4 = 'Credit 4'

caption1 = Label(text=msg1, **label_opts1)
caption2 = Label(text=msg2, **label_opts2)
caption3 = Label(text=msg3, **label_opts3)
caption4 = Label(text=msg4, **label_opts4)

p.add_layout(caption1, 'below')
p.add_layout(caption2, 'below')
p.add_layout(caption3, 'below')
p.add_layout(caption4, 'below')

# add button "directly" below the figure that highlights the wasted space
button = Button(label="Button", button_type="primary")

layout = column(p, button)

show(layout)
```


#### Screenshots or screencasts of the bug in action
<img width="766" alt="Screen Shot 2020-08-12 at 9 26 44 PM" src="https://user-images.githubusercontent.com/6944089/90084253-d86d9000-dce2-11ea-9066-9722c7011aa7.png">

Is there any way to set the height of those Labels to 0? Don't have them take up any space below the figure and have the text still appear? Or a way to add annotations outside the bounds of the figure without using CSS so that the text still appears when you save the figure? Passing height to Label doesn't work and exploring its various parameters doesn't show any obvious way of manually controlling that.

It seems like add_layout puts things into a list of p.below, but I can't find any documentation on how the list of things in p.below gets turned into a figure, how the heights of things in the list are chosen, etc. 
user: ethankruse
issue_10390: state: open
title: Add tests for recent regression and bugfix PRs
body: - [ ] https://github.com/bokeh/bokeh/pull/10366
- [ ] https://github.com/bokeh/bokeh/pull/10378
- [ ] https://github.com/bokeh/bokeh/pull/10373
- [x] https://github.com/bokeh/bokeh/pull/10371
- [ ] https://github.com/bokeh/bokeh/pull/10148
- [ ] https://github.com/bokeh/bokeh/pull/10372
- [ ] https://github.com/bokeh/bokeh/pull/10382
- [ ] https://github.com/bokeh/bokeh/pull/10396
- [ ] https://github.com/bokeh/bokeh/pull/10415

user: mattpap
issue_10387: state: open
title: [BUG] Setting zoom range from Bokeh server has race condition resulting in inconsistent results
body: #### Software version info
Python==3.7.7
bokeh==2.1.1
MacOS==10.15.6
Safari==13.1.2 and Chrome==84.0.4147.125

#### Description of expected behavior and the observed behavior
In a Bokeh server, in the case where a figure's zoom has already been set by the server once, setting the figure's zoom range on the server side again (during a callback handler), the displayed zoom range may become either the first set zoom or the newly set zoom.

#### Complete, minimal, self-contained example code that reproduces the issue
The below minimal example creates a plot and sets the zoom. On the button click, the data is updated and the zoom changed from the server side. The button click will always update the data. However, the zoom will end up in one of 3 possible outcomes. The original zoom (0, 4), the new zoom (3, 6), or a partially updated zoom (0, 6). On my machine, the resulting zoom was most often the original zoom (0, 4). Rapidly clicking the button often shows it flickering back and forth between the multiple zoom possibilities.

Run using `bokeh serve example.py`.
```
import numpy as np
import pandas as pd
from bokeh.models import Button
from bokeh.plotting import Figure, curdoc
from bokeh.layouts import column
from bokeh.models.sources import ColumnDataSource

def dashboard():
    data0 = pd.DataFrame({'y': [1, 2, 3] * 5, 'x': np.arange(15)})
    data1 = pd.DataFrame({'y': [4, 4, 5] * 5, 'x': np.arange(15) + 1})
    data_source = ColumnDataSource(data0)

    figure = Figure()
    figure.line('x', 'y', source=data_source)
    figure.y_range.start = 0
    figure.y_range.end = 4

    def updata_data():
        new_data = ColumnDataSource.from_df(data1)
        data_source.data.update(**new_data)
        figure.y_range.start = 3
        figure.y_range.end = 6

    button = Button()
    button.on_click(updata_data)

    curdoc().add_root(column(button, figure))

dashboard()
```

#### Screenshots or screencasts of the bug in action
![zoom_issue](https://user-images.githubusercontent.com/38118756/89930340-9bb57200-dbd8-11ea-94bc-cbc8d4633c12.gif)

#### Additional notes
Suggestions of a temporary workaround to ensure the zoom update from the server always takes affect would be appreciated.
user: mattrobin
issue_10383: state: open
title: [BUG] Patches with nan separators seemingly make repeated overlapping patches
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
Python 3.7.0
Bokeh 2.1.1
Geopandas 0.6.1
Shapely 1.6.4

#### Description of expected behavior and the observed behavior
Given data consisting of multiple polygons such as the boundaries of a country (with islands etc).  If the x (and y) boundaries are in one list, with `nan` separating each polygon, then the `figure.patches()` command generates glyphs that do not have the correct transparency level as set by the `fill_alpha` parameter.  It looks like patches makes multiples of all of the patches and overlays them, resulting in a darker (more opaque) appearance than there should be.  

So if one has multiple countries. Each consisting of multiple polygons separated by injecting `nan` on the list of x and y patch coordinates in a `ColumnDataSource`.  Assigns a color each country and sets `fill_alpha` less than 1, and uses patches to color fill each country.  Then countries consisting of multiple polygons will be more opaque (darker) than those with fewer or only one.  

This problem does not arise if one uses properly formatted data in `ColumnDataSource' and uses 'figure.multi_polygons()' instead. 

#### Complete, minimal, self-contained example code that reproduces the issue
This is a rather minimal example, loading a world map shape file, and showing the difference between plotting a country defined by many polygons using 

* 0: Multiple patches with multiple colors 
* 1: Multiple patches with one color (what's desired but would like to use only one entry in `ColumnDataSource`
* 2: One patch with one color with `nan` separating the polygons (The BUG)
* 3: The workaround of using `multi_polygons` instead to get the desired/intended structure and effect.
 
```python
import geopandas as gpd

from shapely.geometry.polygon import Polygon
from shapely.geometry.multipolygon import MultiPolygon

from bokeh.io       import show, output_file
from bokeh.models   import ColumnDataSource, MultiPolygons
from bokeh.models   import ColorMapper, LinearColorMapper 
from bokeh.palettes import Turbo256 as palette
from bokeh.plotting import figure
from bokeh.layouts  import row, column

output_file("tmp.html")

# Data from: https://www.naturalearthdata.com/downloads/10m-cultural-vectors/
map_world_gpd = gpd.read_file('map_data/ne_10m_admin_0_countries/ne_10m_admin_0_countries.shp')
map_world_gpd.set_index('NAME_EN',inplace=True)
map_world_gpd = map_world_gpd.to_crs(epsg=3395)

# Select a country as a source (use one with multiple polygons)
country =  'Indonesia' #'United Kingdom' #'Australia' # 'Italy'
gsource = map_world_gpd.loc[country]['geometry']


def get_PolyCoords(poly,coord_type='x'):
    """Returns the coordinates ('x' or 'y') of """
    
    if coord_type == 'x':
        # Get the x coordinates of the exterior
        return list( poly.exterior.coords.xy[0] )
    elif coord_type == 'y':
        # Get the y coordinates of the exterior
        return list( poly.exterior.coords.xy[1] )
    
def get_MultiPoly(mpoly,coord_type='x'):
    """Returns the coordinates ('x' or 'y') for the exterior and interior of MultiPolygon digestible by multi_polygons in Bokeh"""
    
    if coord_type == 'x':
        i=0
    elif coord_type == 'y':
        i=1
    
    # Get the x or y coordinates
    c = [] 
    if isinstance(mpoly,Polygon):
        mpoly = [mpoly]
    for poly in mpoly: # the polygon objects return arrays, it's important they be lists or Bokeh fails
        exterior_coords = poly.exterior.coords.xy[i].tolist();
        
        interior_coords = []
        for interior in poly.interiors:
            if isinstance(interior.coords.xy[i],list):
                interior_coords += [interior.coords.xy[i]];
            else:
                interior_coords += [interior.coords.xy[i].tolist()];
        c.append([exterior_coords, *interior_coords])
    return c
    
    
def get_MultiPolyCords(mpoly,coord_type='x'):
    """Returns the coordinates ('x' or 'y') of edges of a MultiPolygon exterior"""
    if isinstance(mpoly,Polygon):
        mpoly = [mpoly]
    if coord_type == 'x':
        # Get the x coordinates of the exterior
        x = [] 
        for poly in mpoly:
            x.append(get_PolyCoords(poly,coord_type))
        return x
    elif coord_type == 'y':
        # Get the y coordinates of the exterior
        y = [] 
        for poly in mpoly:
            y.append(get_PolyCoords(poly,coord_type))
        return y
    
def concat_MultiPolyCords(mpoly,coord_type='x'):
    """Returns the coordinates ('x' or 'y') of edges of a MultiPolygon exterior"""
    if isinstance(mpoly,Polygon):
        mpoly = [mpoly]
    if coord_type == 'x':
        # Get the x coordinates of the exterior
        x = [] 
        for poly in mpoly:
            x.append(float('NaN'))
            x = x+ get_PolyCoords(poly,coord_type)
        return x
    elif coord_type == 'y':
        # Get the y coordinates of the exterior
        y = [] 
        for poly in mpoly:
            y.append(float('NaN'))
            y = y+ get_PolyCoords(poly,coord_type)
        return y
    

def plen(mpoly):
    if isinstance(mpoly,Polygon):
        return 1
    else:
        return len(mpoly)

# Make data for a patch for each polygon in a Multipolygon, all different colors
source0 = ColumnDataSource({
    'x': get_MultiPolyCords(gsource,'x'),
    'y': get_MultiPolyCords(gsource,'y'),
    'n': list(range(0,plen(gsource))),
    })

# Make data for a patch for each polygon in a Multipolygon, all same color
source1 = ColumnDataSource({
    'x': get_MultiPolyCords(gsource,'x'),
    'y': get_MultiPolyCords(gsource,'y'),
    'n': [0]*plen(gsource),
    })

# Make data for a patch for each Multipolygon, separating polygons with nan 
source2 = ColumnDataSource({
    'x': [concat_MultiPolyCords(gsource,'x')],
    'y': [concat_MultiPolyCords(gsource,'y')],
    'n': [0],
    })
# Make data for Multipolygon, using one Multipolygon for all
source3 = ColumnDataSource({ 
    'x': [get_MultiPoly(gsource,'x')],
    'y': [get_MultiPoly(gsource,'y')],
    'n': [0],
    })

# Initialize our figures
p0 = figure(match_aspect=True,aspect_ratio=2,x_axis_location=None, y_axis_location=None,)
p1 = figure(match_aspect=True,aspect_ratio=2,x_axis_location=None, y_axis_location=None,)
p2 = figure(match_aspect=True,aspect_ratio=2,x_axis_location=None, y_axis_location=None,)
p3 = figure(match_aspect=True,aspect_ratio=2,x_axis_location=None, y_axis_location=None,)


# Plot 
p0.title.text = "Map has "+str(plen(gsource))+" polygons, patched using "+str(plen(source1.data['x']))+" patches, different colors"
p0.patches('x', 'y', source=source0,
          fill_color={'field': 'n', 'transform': LinearColorMapper(palette=palette,low=0,high=plen(gsource))},
          fill_alpha=0.5, line_color="black", line_width=0.5)

p1.title.text = "Map has "+str(plen(gsource))+" polygons, patched using "+str(plen(source1.data['x']))+" patches, same color"
p1.patches('x', 'y', source=source1,
          fill_color={'field': 'n', 'transform': LinearColorMapper(palette=palette,low=0,high=plen(gsource))},
          fill_alpha=0.5, line_color="black", line_width=0.5)

p2.title.text = "Map has "+str(plen(gsource))+" polygons, patched using "+str(plen(source2.data['x']))+" patches using NaN separators (too dark, BUG)"
p2.patches('x', 'y', source=source2,
          fill_color={'field': 'n', 'transform': LinearColorMapper(palette=palette,low=0,high=plen(gsource))},
          fill_alpha=0.5, line_color="black", line_width=0.5)

p3.title.text = "Map has "+str(plen(gsource))+" polygons, using "+str(len(source3.data['x']))+" multipolygon, same color"
p3.multi_polygons(xs='x',ys='y', source=source3,
          fill_color={'field': 'n', 'transform': LinearColorMapper(palette=palette,low=0,high=plen(gsource))},
          fill_alpha=0.5, line_color="black", line_width=0.5)

show(column(p0,p1,p2,p3,sizing_mode = 'scale_width'))
```

#### Stack traceback and/or browser JavaScript console output
Not relevant (no errors)

#### Screenshots or screencasts of the bug in action
See attached.
![Bokeh Plot](https://user-images.githubusercontent.com/60117554/89566503-43ecc480-d7ee-11ea-8c84-0437e4f24f72.png)

user: thedrdos
issue_10376: state: open
title: ColorBar should display low/high color values of LinearColorMapper
body: Hi,

I have a heatmap style plot based on the [les_mis.py](https://docs.bokeh.org/en/latest/docs/gallery/les_mis.html) example.

I am configuring the `LinearColorMapper` with a `low_color` value and the plot shows it correctly, but the `ColorBar` does not, even if I explicitly give it a `FixedTicker`

```python
import pandas as pd
import numpy as np
import bokeh.palettes as palettes
from bokeh.plotting import figure
from bokeh.plotting import show
from bokeh.models import LinearColorMapper, ColorBar, FixedTicker
from bokeh.transform import transform

data = pd.DataFrame(
    np.arange(0.1, 1.0, 0.1).reshape(3, 3),
    index=["a", "b", "c"], columns=["x", "y", "z"]
)

ii, jj = np.meshgrid(data.columns, data.index, indexing="ij")
ii = ii.flatten()
jj = jj.flatten()
cc = [data.loc[j, i] for i, j in zip(ii, jj)]
plot_data = dict(
    xname=ii,
    yname=jj,
    colors=cc,
    data=cc,
)

colormapper = LinearColorMapper(
    palette=palettes.magma(7)[::-1], low=0.3, high=1.0, low_color="white"
)

fig = figure(
    x_axis_location="above",
    tools="hover",
    x_range=list(data.columns),
    y_range=list(reversed(data.index)),
    tooltips=[
        ('names', '@yname, @xname'), ('data', '@data{0.0}')
    ],
)

fig.rect(
    'xname',
    'yname',
    1,
    1,
    source=plot_data,
    color=transform("colors", colormapper),
)

# ticks = [0.0]
# ticks.extend(np.arange(0.3, 1.0, 0.1))
color_bar = ColorBar(
    color_mapper=colormapper,
    location=(0, 0),
#     ticker=FixedTicker(ticks=ticks)
)

fig.add_layout(color_bar, 'right')

show(fig)
```

This is the figure I get with the above code:

![plot showing issue](https://user-images.githubusercontent.com/4750021/89425938-7dabc580-d731-11ea-84ab-73137d504b8b.png)

Is this the expected behaviour or a bug?

I've tried this on bokeh 2.1.1 with python 3.7.2

Thanks

user: pablo-esteban
issue_10369: state: open
title:   [BUG] a bug specific to the combination of match_aspect=True and "stretch" sizing modes
body: ### FOLLOW THESE INSTRUCTIONS CAREFULLY

*ISSUES THAT DO NOT CONTAIN NECESSARY INFORMATION MAY BE CLOSED, IMMEDIATELY*

The issue tracker is NOT the place for general support. For questions and technical assistance, come join the [Bokeh  Project Discourse](https://discourse.bokeh.org).

For defects or deficiencies, please provide *ALL OF THE FOLLOWING*:

#### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
bokeh=2.1.1
python=3.8.3
OS=windows 10
brower=Firefox 79
#### Description of expected behavior and the observed behavior
Expect to keep the aspect ratio of image as is when the browser window is resized.
However, the aspect ratio of image will change when the browser window is resized.
#### Complete, minimal, self-contained example code that reproduces the issue

```
# code goes here between backticks
from bokeh.plotting import figure, show
from bokeh.layouts import layout
p = figure(match_aspect=True)
p.image_url(['https://static.bokeh.org/logos/logo.png'],0,0,1,1)
layer = layout(p, sizing_mode='stretch_both')
show(layer)
```

#### Stack traceback and/or browser JavaScript console output

#### Screenshots or screencasts of the bug in action
![bokeh_plot](https://user-images.githubusercontent.com/69213863/89328205-b26a3f00-d695-11ea-8a1d-3db8dbe1c4e0.png)

user: user4015990
issue_10345: state: open
title: [BUG] Uncaught (in promise) TypeError: Cannot read property 'connect' of undefined
body: This appears to be related to https://github.com/bokeh/bokeh/issues/9218

When I add a dropdown with a callback to a Bokeh chart, then in the browser it causes a Javascript error.

I'm very new to Bokeh, so it is quite possible that I am doing something wrong, though I suspect that this isn't the intended behaviour anyway.  I'd be glad to hear of any workarounds I can do.

I'm trying to add a dropdown that allows users to choose which groups of data will be shown in the chart (roughly similar to the way you can click on the legend to show hide things).


#### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)

bokeh==2.1.1 
Ubuntu and OSX
Python 3.8
Latest Chrome and Chrome Canary, latest Firefox (slightly different error in Firefox `Uncaught (in promise) TypeError: n is undefined`)

#### Description of expected behavior and the observed behavior

I expect to see the chart on the page.  I don't see the chart on the page.

If I add a callback to the dropdown, it breaks.  If I don't add the callback, it works.

#### Complete, minimal, self-contained example code that reproduces the issue

```
from bokeh.io import show
from bokeh.models import Dropdown, ColumnDataSource, CustomJS
from bokeh.plotting import figure
from bokeh.layouts import column, row

p = figure()
source_line = ColumnDataSource(dict(x = [1, 2], y = [2, 1]))
source_circle = ColumnDataSource(dict(x = [1, 2], y = [2, 1]))
line = p.line('x', 'y', source = source_line)
circle = p.circle('x', 'y', source = source_circle)

dropdown = Dropdown(label="Lines or Circles", button_type="warning", menu=['Lines', 'Circles'])

callback = CustomJS(code='console.log("Callback!")')
dropdown.js_on_change('value', callback)

layout = row(
    column(dropdown),
    column(p)
)

show(layout)
```

#### Stack traceback and/or browser JavaScript console output

From Chrome

```
bokeh-2.1.1.min.js:176 Uncaught (in promise) TypeError: Cannot read property 'connect' of undefined
    at m.connect (bokeh-2.1.1.min.js:176)
    at m._update_property_callbacks (bokeh-2.1.1.min.js:239)
    at m.connect_signals (bokeh-2.1.1.min.js:239)
    at Function._initialize_references_json (bokeh-2.1.1.min.js:162)
    at Function.from_json (bokeh-2.1.1.min.js:162)
    at g (bokeh-2.1.1.min.js:160)
    at bokeh-2.1.1.min.js:160
    at bokeh-2.1.1.min.js:180
```

From Firefox

```
Uncaught (in promise) TypeError: n is undefined
    connect https://cdn.bokeh.org/bokeh/release/bokeh-2.1.1.min.js:176
    _update_property_callbacks https://cdn.bokeh.org/bokeh/release/bokeh-2.1.1.min.js:239
    connect_signals https://cdn.bokeh.org/bokeh/release/bokeh-2.1.1.min.js:239
    _initialize_references_json https://cdn.bokeh.org/bokeh/release/bokeh-2.1.1.min.js:162
    from_json https://cdn.bokeh.org/bokeh/release/bokeh-2.1.1.min.js:162
    g https://cdn.bokeh.org/bokeh/release/bokeh-2.1.1.min.js:160
    embed_items https://cdn.bokeh.org/bokeh/release/bokeh-2.1.1.min.js:160
    defer https://cdn.bokeh.org/bokeh/release/bokeh-2.1.1.min.js:180
```

user: Singletoned
issue_10331: state: open
title: [BUG] Bokeh trying to use chromedriver when geckodriver installed.
body: I have followed the instructions here: https://docs.bokeh.org/en/latest/docs/user_guide/export.html?highlight=export

And conda installed firefox, geckodriver, selenium.

When trying to export_png, I get the following error:

```
~/miniconda3/envs/braid/lib/python3.7/site-packages/bokeh/io/export.py in get_screenshot_as_png(obj, driver, timeout, resources, width, height)
    202         web_driver.maximize_window()
    203         web_driver.get("file:///" + tmp.path)
--> 204         wait_until_render_complete(web_driver, timeout)
    205         [width, height, dpr] = _maximize_viewport(web_driver)
    206         png = web_driver.get_screenshot_as_png()

~/miniconda3/envs/braid/lib/python3.7/site-packages/bokeh/io/export.py in wait_until_render_complete(driver, timeout)
    294     except TimeoutException as e:
    295         _log_console(driver)
--> 296         raise RuntimeError('Bokeh was not loaded in time. Something may have gone wrong.') from e
    297 
    298     driver.execute_script(_WAIT_SCRIPT)

RuntimeError: Bokeh was not loaded in time. Something may have gone wrong.
```

When I run debug, the driver bokeh is trying to use is chromedriver.

![Screenshot from 2020-07-24 18-29-11](https://user-images.githubusercontent.com/1796208/88442849-a0f27e80-cddb-11ea-82b2-5638ab350579.png)

I do also have chromedriver on my path, but not in my conda environment.

After uninstalling chromedriver from my global path, things worked as expected. Not exactly sure what a good solution is here. It's a conflict between my conda environment and the global...
user: birdsarah
issue_10326: state: open
title: [FEATURE] Colorbar - title positioning
body: **Is your feature request related to a problem? Please describe.**
When using colorbar as a legend in bokeh charts, fixed horizontal legend title leaves chart area if it's too long.

![image](https://user-images.githubusercontent.com/20476096/88000633-fca6ca00-cab2-11ea-8f95-698518818536.png)

**Describe the solution you'd like**
I would prefer an option to align the title (vertical/horizontal), and also position relative to the actual colorbar(left, right, top, below) if possible

**Describe alternatives you've considered**
adding label separate from the color_bar using add_layout, similar to (https://github.com/bokeh/bokeh/issues/7093). Seems hacky though, since now that the colorbar itself supports title.


user: AjayThorve
issue_10322: state: open
title: [BUG] HoverTool doesn't take on theme of plot
body: When using `curdoc().theme = 'dark_minimal'` to set the theme of a plot, the theme is not applied to the HoverTool. I'd expect it to have a dark background, in this case.

bokeh 2.1.1
Python 3.6.9
Chrome 84.0.4147.89
Google Colab

<img width="1014" alt="Screen Shot 2020-07-17 at 7 20 21 PM" src="https://user-images.githubusercontent.com/14302923/87842644-b5c29580-c862-11ea-98b8-fcdc71282234.png">
user: rchurt
issue_10300: state: open
title: [FEATURE] add "template" to io.export_png
body: **Is your feature request related to a problem? Please describe.**
For our use-case we need to be able to generate png files with io.export_png that look exactly like the interactive html. Currently this means that we can not make use of the very nice and extremely powerful 'template' functionality that io.save provides.

**Describe the solution you'd like**
It would be fantastic if the 'template' functionality could also be added to io.export_png

user: fstalm
issue_10299: state: open
title: Interactive/Selectable labels and text glyphs
body: Hi, (1) is it possible to make titles or labels or text be clickable to open URLs?  (2) is it possible to make titles or labels be searchable by a browser?
Best Regards!
user: gudeqing
issue_10296: state: open
title: Asynchronous initialization may result in an invalid view state
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
ubuntu 20.04, bokeh 2.2.1, python 3.8

#### Description of expected behavior and the observed behavior

The function `core/dom.st: toggle_attributes` sometimes crashes. This occurs when the toggle is called prior to a widget's render being called.In such a case the widget's `control` function may yield undefined objects which the `toggle_attributes` does not handle.

I'm not confortable with javascript but it would seem that solutions are any of:
1.  `widget.control` should check the existence of the attributes it yields
2. `ControlView.connect_signal` should check that the elements it sends to `toggle_attributes` are not undefined
3. `toggle_attributes` should check that the element it toggles is not undefined.
 
#### Complete, minimal, self-contained example code that reproduces the issue

Unable to reproduce in example code. This is likely only occurs because our code is adding/removing root elements to the doc.

#### Stack traceback and/or browser JavaScript console output
```
Uncaught TypeError: el is undefined
    toggle_attribute http://localhost:4338/static/js/bokeh.js?v=51072279d45735a2ccdad6e01a97bdaf:6803
    connect_signals http://localhost:4338/static/js/bokeh-widgets.js?v=a249ca74e8c7eb4917776ed1e353c018:197
    new_slot http://localhost:4338/static/js/bokeh.js?v=51072279d45735a2ccdad6e01a97bdaf:6472
    emit http://localhost:4338/static/js/bokeh.js?v=51072279d45735a2ccdad6e01a97bdaf:2507
    emit http://localhost:4338/static/js/bokeh.js?v=51072279d45735a2ccdad6e01a97bdaf:2516
    _setv http://localhost:4338/static/js/bokeh.js?v=51072279d45735a2ccdad6e01a97bdaf:2192
    setv http://localhost:4338/static/js/bokeh.js?v=51072279d45735a2ccdad6e01a97bdaf:2223
    apply_json_patch http://localhost:4338/static/js/bokeh.js?v=51072279d45735a2ccdad6e01a97bdaf:1213
    _handle_patch http://localhost:4338/static/js/bokeh.js?v=51072279d45735a2ccdad6e01a97bdaf:37907
    handle http://localhost:4338/static/js/bokeh.js?v=51072279d45735a2ccdad6e01a97bdaf:37861
    _steady_state_handler http://localhost:4338/static/js/bokeh.js?v=51072279d45735a2ccdad6e01a97bdaf:37680
    _current_handler http://localhost:4338/static/js/bokeh.js?v=51072279d45735a2ccdad6e01a97bdaf:37665
    _on_message http://localhost:4338/static/js/bokeh.js?v=51072279d45735a2ccdad6e01a97bdaf:37640
    onmessage http://localhost:4338/static/js/bokeh.js?v=51072279d45735a2ccdad6e01a97bdaf:37516
bokeh.js:6803:13
```

#### Screenshots or screencasts of the bug in action

user: poldavezac
issue_10290: state: open
title: Test for excessive toolbar re-render
body: https://github.com/bokeh/bokeh/pull/10288 fixed an excessive toolbar re-render, but evidently this issue has come and gone a few times. This doesn't seem to be something a visual image test could capture but @mattpap suggests some kind of quick/dirty unit test could be possible and useful. 
user: bryevdv
issue_10276: state: open
title: [FEATURE] Tighter integration with React
body: 
**Is your feature request related to a problem? Please describe.**
Bokeh integration with React has a lot of potential, one of them being ability to leverage awesome libraries like [react-grid-layout](https://github.com/STRML/react-grid-layout), but I could not find any usable examples of bokeh templates working with react.

I ended up writing a couple of examples demonstrating the same here - (https://github.com/AjayThorve/bokeh/tree/react-grid-layout-example/examples/app/dash-react-grid-layout), which prompted me to open a discussion about the possibilities of a tighter integration with react!

Also, I would appreciate a review of those examples and suggestions of if and where can they be added to bokeh repo/docs.


**Describe the solution you'd like**
Open to discussion - one of the suggestions -> Ability to serve a react application with a bokeh backend natively? 

**Describe alternatives you've considered**
Merging html templates with jinja server-side rendering along with precomplied react

cc: @bryevdv 
user: AjayThorve
issue_10268: state: open
title: [BUG] Themes aren't applied using components starting in 2.1.x but work in 2.0.x
body: Using a theme (e.g. `curdoc().theme = Theme(json={'attrs': {'Title': {'text_color': 'red'}}})`) works fine when using `plotting.save` in both 2.0.x and 2.1.x. However, when using components, the theme is no longer applied starting in 2.1.x.

The minimal working example is below. Expected output is the title text in red because of the theme saying so. I copy the necessary html header and footer stuff created by `plotting.save` to create a complete html page and put the resulting components script/div stuff between them. I use the header calling the proper CDN bokehjs version depending on which bokeh version I'm testing, but that doesn't seem to matter.

When run in 2.0.x, the components file also has a red title text, properly using the theme. However, 2.1.x ignores anything from themes and creates the same file except with default text color in the title.


```
from bokeh import plotting
from bokeh.embed import components
from bokeh.io import curdoc
from bokeh.themes import Theme


curdoc().theme = Theme(json={'attrs': {'Title': {'text_color': 'red'}}})

plotting.output_file("save.html")

p = plotting.figure(plot_width=400, plot_height=400)

# add a line renderer
p.line([1, 2, 3, 4, 5], [6, 7, 2, 4, 5], line_width=2)

p.title.text = 'Red Title'

plotting.save(p)


htmlhead20 = """
<!DOCTYPE html>
<html lang="en">
  <head>
      <meta charset="utf-8">
      <title>Bokeh Plot</title>      
        <script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-2.0.2.min.js" integrity="sha384-ufR9RFnRs6lniiaFvtJziE0YeidtAgBRH6ux2oUItHw5WTvE1zuk9uzhUU/FJXDp" crossorigin="anonymous"></script>
        <script type="text/javascript">
            Bokeh.set_log_level("info");
        </script>
  </head>
  <body>
"""

htmlhead21 = """
<!DOCTYPE html>
<html lang="en">
  <head>
      <meta charset="utf-8">
      <title>Bokeh Plot</title>      
        <script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-2.1.1.min.js" integrity="sha384-kLr4fYcqcSpbuI95brIH3vnnYCquzzSxHPU6XGQCIkQRGJwhg0StNbj1eegrHs12" crossorigin="anonymous"></script>
        <script type="text/javascript">
            Bokeh.set_log_level("info");
        </script>
  </head>
  <body>
"""

htmlfoot = """  
  </body>
  
</html>
"""



script, div = components(p)

with open('components20.html', 'w') as ff:
    ff.write(htmlhead20)
    ff.write(script)
    ff.write(div)
    ff.write(htmlfoot)

"""
with open('components21.html', 'w') as ff:
    ff.write(htmlhead21)
    ff.write(script)
    ff.write(div)
    ff.write(htmlfoot)
"""
```


user: ethankruse
issue_10261: state: open
title: Embedded Bokeh server doc should expose hook for error reporting
body: Based on comment from @bryevdv on [Bokeh Discourse discussion](https://discourse.bokeh.org/t/how-to-get-embedded-bokeh-server-doc-to-report-page-error-to-flask-server/5918/2?u=jonas_grave_kristens) forum I am creating this feature request. 

The setup is a Bokeh server and Flask server where the flask server is serving embedded `server_document`s. If for some reason the Bokeh server fails to GET a given document and reports a page error, eg 403, then I would like to capture that and by a Flask template inform the the user of an issue instead of the user just looking on the blank page.

I have tried to add routes like `@app.errorhandler(403)` but also `@app.teardown_request` in order to try to capture any page error but so far no luck. If I understand correctly as long as flask is able to create the embedded template (the html doc) then that would not return any page error.

To show what I mean I have taken the Flask embed example from [git](https://github.com/bokeh/bokeh/tree/branch-2.2/examples/embed/arguments) and used that. In the `flask_server.py` script I have added some error handling routes mentioned above but they are not run if Bokeh server reports a eg 403 GET error.

I first start the bokeh server app:
```
bokeh serve bokeh_server.py --allow-websocket-origin=127.0.0.1:5000
```
then I start the flask app
```
python flask_server.py
```

When running this and going to `localhost:5000`, when one chooses a link I get to the `app_html` page but no embedded plot is shown due to 403 page error reported by Bokeh server. I would like to capture this error and show to the user that there is a page error - how do I do that?

Firefox console:
```
Request URL:http://localhost:5006/bokeh_server/ws?bokeh-protocol-version=1.0&bokeh-session-id=MgpZNLFpFlZDDacVqZzjLlWZHmcjCngpWn19QiIZnyXs
Request method:GET
Remote address:127.0.0.1:5006
Status code:
403
Version:HTTP/1.1
```

Have tested in Bokeh version 1.3.4 (py2.7) and 2.1.1 (py3.6.8), both on Redhat 7.

Unfortunately my limited java script skills are limiting me from putting forward a proposal for a solution.

---
### The scripts:
**bokeh_server.py**
```
#!/usr/bin/env python

'''This example demonstrates embedding an autoloaded Bokeh server
into a simple Flask application, and passing arguments to Bokeh.

To view the example, run:

    python flask_server.py

in this directory, and navigate to:

    http://localhost:5000

'''
import numpy as np

from bokeh.io import curdoc
from bokeh.layouts import column, row
from bokeh.models import ColumnDataSource, Slider, TextInput
from bokeh.plotting import figure

# Retrieving the arguments
args = curdoc().session_context.request.arguments

try:
    batchid = int(args.get('batchid')[0])
except (ValueError, TypeError):
    batchid = 1

func = {
    1 : np.cos,
    2 : np.sin,
    3 : np.tan
}[batchid]

# Set up data
N = 200
x = np.linspace(0, 4*np.pi, N)
y = func(x)
source = ColumnDataSource(data=dict(x=x, y=y))

# Set up plot
plot = figure(plot_height=400, plot_width=400, title="my wave",
              tools="crosshair,pan,reset,save,wheel_zoom",
              x_range=[0, 4*np.pi], y_range=[-2.5, 2.5])

plot.line('x', 'y', source=source, line_width=3, line_alpha=0.6)

# Set up widgets
text = TextInput(title="title", value="Batch n°{}".format(batchid))
offset = Slider(title="offset", value=0.0, start=-5.0, end=5.0, step=0.1)
amplitude = Slider(title="amplitude", value=1.0, start=-5.0, end=5.0)
phase = Slider(title="phase", value=0.0, start=0.0, end=2*np.pi)
freq = Slider(title="frequency", value=1.0, start=0.1, end=5.1)

# Set up callbacks
def update_title(attrname, old, new):
    plot.title.text = text.value

text.on_change('value', update_title)

def update_data(attrname, old, new):

    # Get the current slider values
    a = amplitude.value
    b = offset.value
    w = phase.value
    k = freq.value

    # Generate the new curve
    x = np.linspace(0, 4*np.pi, N)
    y = a*func(k*x + w) + b

    source.data = dict(x=x, y=y)

for w in [offset, amplitude, phase, freq]:
    w.on_change('value', update_data)

# Set up layouts and add to document
inputs = column(text, offset, amplitude, phase, freq)

curdoc().add_root(row(inputs, plot, width=800))
```

**flask_server.py**
```
'''This example demonstrates embedding an autoloaded Bokeh server
into a simple Flask application, and passing arguments to Bokeh.

To view the example, run:

    python flask_server.py

in this directory, and navigate to:

    http://localhost:5000

'''
import atexit
import subprocess

from flask import Flask, render_template_string

from bokeh.embed import server_document

home_html = """
<!DOCTYPE html>
<html lang="en">
  <body>
    <div class="bk-root">
      <h1><a href="/batch/1"> Batch 1 (cos)</a></h1>
      <h1><a href="/batch/2"> Batch 2 (sin)</a></h1>
      <h1><a href="/batch/3"> Batch 3 (tan)</a></h1>
    </div>
  </body>
</html>
"""

app_html = """
<!DOCTYPE html>
<html lang="en">
  <body>
    <div>
        <h2><a href="/batch/1">Batch 1 (cos)</a> - <a href="/batch/2">Batch 2 (sin)</a> - <a href="/batch/3">Batch 3 (tan)</a></h2>
    </div>
    {{ bokeh_script|safe }}
  </body>
</html>
"""

err_html = """
<!DOCTYPE html>
<html lang="en">
  <body>
    <div>
        <h2>{{ error }}</h2>
    </div>
  </body>
</html>
"""

app = Flask(__name__)

#bokeh_process = subprocess.Popen(
#    ['python', '-m', 'bokeh', 'serve', '--allow-websocket-origin=localhost:5000', 'bokeh_server.py'], stdout=subprocess.PIPE)

@atexit.register
def kill_server():
    bokeh_process.kill()

@app.route('/')
def home():
    return render_template_string(home_html)

@app.route('/batch/<int:batchid>')
def visualization(batchid):
    bokeh_script = server_document(url='http://localhost:5006/bokeh_server', arguments=dict(batchid=batchid))
    return render_template_string(app_html, bokeh_script=bokeh_script)

@app.errorhandler(404)
def page_not_found(e):
  return render_template_string(err_html, error = "404 Not found")

@app.errorhandler(403)
def page_not_found(e):
  return render_template_string(err_html, error = "403 Forbidden")

@app.teardown_request
def teardown_request_func(error=None):
   print("teardown_request is running")
   if error:
      # log error
      print(str(error))


if __name__ == '__main__':
    app.run(debug=True)
```


user: carve11
issue_10248: state: open
title: Add support for rendering in environments lacking DOM
body: In particular support:

1. WebWorkers
2. Node with a canvas polyfill
user: mattpap
issue_10232: state: open
title: Please, add possibility to specify 'auto' for  LayoutDOM's margin type:feature
body: LayoutDOM is defined here
https://github.com/bokeh/bokeh/blob/6e3819dfb7b48b911f7d9f256290c9299803d6e4/bokeh/models/layouts.py#L78

I think the idea should be something like modifying in this way>
```
margin = Tuple(Either(Int, Auto), Either(Int, Auto), Either(Int, Auto), Either(Int, Auto), default=(0, 0, 0, 0)
).accepts(Tuple(Int, Int), lambda v_h: (v_h[0], v_h[1], v_h[0], v_h[1])) \
.accepts(Int, lambda m: (m, m, m, m)) \
.accepts(Tuple(Int, Auto), v_h: (v_h[0], v_h[1], v_h[1], v_h[1])) \
.accepts(Auto, lambda m: (m, m, m, m))
```

So, the dependencies will not be broken + this will add possibility to easily center plots in the page based on the plot's parent container

Use-case example:
`bokeh.plotting.figure(title="some figure", plot_width=600, plot_height=600, margin=(0, 'auto'))`
or
`bokeh.plotting.figure(title="some figure", plot_width=600, plot_height=600, margin=(0, 'auto', 0, 'auto'))`

** This code will center the plot horizontally in the page

user: Moldoteck
issue_10211: state: open
title: [BUG] Setting `options` in Select widget doesn't update it's value property
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant  packages)
```bash
$ bokeh info
Python version      :  3.8.1 (default, Dec 27 2019, 18:06:00) 
IPython version     :  7.12.0
Tornado version     :  6.0.3
Bokeh version       :  2.1.0
BokehJS static path :  /usr/local/lib/python3.8/site-packages/bokeh/server/static
node.js version     :  v13.8.0
npm version         :  6.13.7
```
```
OS: Mac OS X Catalina (10.15.5)
Browser: Google Chrome 83.0.4103.106 (Official Build) (64-bit)
```
#### Description of expected behaviour and the observed behaviour
The `value` property should be set automatically when setting the `options` property and should reflect what widget GUI shows.
Now the `value` property is not set automatically and must be set manually otherwise there will be inconsistency between what the `value` is and what GUI show

#### Complete, minimal, self-contained example code that reproduces the issue

```
from bokeh.models import Row, CustomJS, Button, Select
from bokeh.plotting import show

select = Select()
button_add = Button(label='Add')  

code_button_add = '''
    select.options = ['option' + select.options.length].concat(select.options)
    console.log(select.value) '''
button_add.js_on_click(CustomJS(args={'select': select}, code = code_button_add))

show(Row(select, button_add))

```

user: tony-bony
issue_10210: state: open
title: [BUG] Bokeh app does not display in notebook cell the first try on Chrome OS Linux; net::ERR_CONNECTION_REFUSED
body: Hello, I first brought this up [over at discourse](https://discourse.bokeh.org/t/bokeh-app-does-not-display-in-jupyter-notebook-using-python-3-8-3-is-this-a-bug/5729) and after some initial investigation Bryan advised me to go ahead and open an issue.

Problem replicated using two different models of Chrome OS machines (on the Linux beta) with the following:

bokeh 2.1.0
python 3.7.1, 3.7.3, 3.8.1, and 3.8.3
jup notebook 6.0.3
Chrome OS 83.0.4103.97 and 83.0.4103.112
Chrome browser* 83.0.4103.97 and 83.0.4103.112
*NOTE: only replicates using Chrome browser ON Chrome OS. Does not replicate using Chrome on Windows, or alternative browser on Chrome OS (e.g., I installed Konquerer and the problem does not replicate)

#### Description of expected behavior and the observed behavior

Expected behavior is for the app to display in the notebook cell as usual (notebook served locally on 8888), but it does not display and the console log reports a net::ERR_CONNECTION_REFUSED (example of full console error printed below).

However after the error has occurred, if I right-click in the output cell area for the problem cell and select "Reload frame", the app displays correctly (NOTE: this "Reload frame" does not appear to be the same thing as Ctrl+r, i.e., reloading the whole page). I am wondering if this is evidence of some kind of race condition at work.

#### Complete, minimal, self-contained example code that reproduces the issue

```
import yaml

from bokeh.layouts import column
from bokeh.models import ColumnDataSource, Slider
from bokeh.plotting import figure
from bokeh.themes import Theme
from bokeh.io import show, output_notebook

from bokeh.sampledata.sea_surface_temperature import sea_surface_temperature

output_notebook()

def bkapp(doc):
    df = sea_surface_temperature.copy()
    source = ColumnDataSource(data=df)

    plot = figure(x_axis_type='datetime', y_range=(0, 25),
                  y_axis_label='Temperature (Celsius)',
                  title="Sea Surface Temperature at 43.18, -70.43")
    plot.line('time', 'temperature', source=source)

    def callback(attr, old, new):
        if new == 0:
            data = df
        else:
            data = df.rolling('{0}D'.format(new)).mean()
        source.data = ColumnDataSource.from_df(data)

    slider = Slider(start=0, end=30, value=0, step=1, title="Smoothing by N Days")
    slider.on_change('value', callback)

    doc.add_root(column(slider, plot))

    doc.theme = Theme(json=yaml.load("""
        attrs:
            Figure:
                background_fill_color: "#DDDDDD"
                outline_line_color: white
                toolbar_location: above
                height: 500
                width: 800
            Grid:
                grid_line_dash: [6, 4]
                grid_line_color: white
    """, Loader=yaml.FullLoader))

show(bkapp) # notebook_url="http://localhost:8888" 
```

#### Stack traceback and/or browser JavaScript console output

```
GET http://localhost:39325/autoload.js?bokeh-autoload-element=1003&bokeh-absolute-url=http://localhost:39325&resources=none net::ERR_CONNECTION_REFUSED
```

#### Screenshots or screencasts of the bug in action

![image](https://user-images.githubusercontent.com/4923361/85214897-68c0d180-b33f-11ea-95ab-05be8f4226ba.png)
user: Ricyteach
issue_10206: state: open
title: [FEATURE] Expose InfoPane ("tooltip") as first-class feature
body: Users want to be able to annotate some point or area on the graph by affixing a little box full of information, possibly including HTML formatting, images, etc just like a hover tooltip can have. Over the years users have actually asked somewhat often "can I fix a hover tooltip as a specific" location, but the current hover tooltip is basically a hidden implementation detail so this has not been possible to date.

This work would be to make the answer to that be "Yes" to that ask. Additionally, this new first-class supported `InfoPane` would be able to be re-used by other tools such as a MeasureTool (#10158) or to support implentations of new behaviors e.g. allow affixing a hover tooltip by clicking, so that content can be copied or links clicked (#5724). Ultimately the existing hover tool should be re-implemented to use this new `InfoPane` model to display itself. (Some pure BokehJS APIs may be needed on the model to support special needs of the hover tool)

The main properties that I think it will need are:
* (x, y) position to annotate
* content to render in the at the location
* which side to anchor the InfoPane  i.e. should it be to the left or right, above or below (x, y) point
* should a little "arrow" be drawn (like current hover tool, not an Arrow annotation)
* other styling options (less important for a first cut)

On the Python side it might very roughly be like:
```python
class InfoPane:
    x = Number # need to handle categoricals too tho
    y = Number

    content = String # possibly also List(String) to be able to "stack" mulitple panes in one place 

    anchor = Enum # top, bottom, left or right
    draw_arrow = Bool
```

cc @ayushiitr2901 
user: bryevdv
issue_10205: state: open
title: [FEATURE] Add a DOM-based legend
body: This is a follow on to replace #4213. Rather than trying to automagically resize and place the existing on-canvas legend, I propose to create a new supplemental DOM-based legend that can be placed in a layout or template, separate from the plot canvas. This legend would render a div for each legend item, that each contains a canvas (to render the legend representative) and a span (for the legend label). By default these would be styled to display in a vertical columnar arrangement, but users should be able to target the CSS to obtain different responsive arrangements (e.g. horizontal, multiple rows, etc). 

cc @bokeh/dev 
user: bryevdv
issue_10199: state: open
title: Image Tests and Docs for BokehJS glyphs
body: ref: https://github.com/bokeh/bokeh/issues/9956

This is a continuation of the above issue for stacked areas in BokehJS but applies more generally to any/all BokehJS glyphs that need to come under better testing.

cc @mattpap for any additional details or context you want to add

user: bryevdv
issue_10196: state: open
title: Dev environments no longer load previous version from CDN 
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
Bokeh - Installed using an updated fork of https://github.com/bokeh/bokeh
Python - 3.8.3
OS - Windows 10
Browser - Google Chrome

#### Description of expected behavior and the observed behavior
When the environment variable "BOKEH_RESOURCES=inline" is not set, the HTML uses remote CDN BokehJS resources, and it uses a wrong URL which is   
`<https://cdn.bokeh.org/bokeh/release/bokeh-0+unknown.min.js`  
HTML fails to get these resources, and results in an empty page.

#### Complete, minimal, self-contained example code that reproduces the issue
If the environment variable is already set, then unset it. Then run the command.

```
python examples/plotting/file/iris.py
```

#### Stack traceback and/or browser JavaScript console output

Failed to load resource: the server responded with a status of 403 ()
iris.html:21 Uncaught ReferenceError: Bokeh is not defined
    at iris.html:21
iris.html:49 Uncaught ReferenceError: Bokeh is not defined
    at HTMLDocument.fn (iris.html:49)

#### Screenshots or screencasts of the bug in action
![image](https://user-images.githubusercontent.com/21126219/85051085-ae9d5e80-b1b4-11ea-8b41-1566af3a91bb.png)

PS : I checked with example plots, but I guess it would have the same problem with other plots as well. 


user: pranshukharkwal
issue_10189: state: open
title: [FEATURE] Exporting tables as CSVs 
body: **Is your feature request related to a problem? Please describe.**
Not a `Problem` per say, but i do need this almost everytime i use Bokeh.

**Describe the solution you'd like**
I've seen a lot of JS hacks to export table data as CSV in Bokeh, but i think just like we have a 'Save' toolbar button for graphs it would be great to also have a save button or export for tables too.

**Describe alternatives you've considered**
Using JS scripts and binding to Button widgets, but isn't always consistant.

user: Vel-San
issue_10184: state: open
title: [BUG] Graph shrink problem with Jupyter Notebook on Chrome, IE, Microsoft Edge.
body: We are developing an open-source data preparation library(Dataprep https://github.com/sfu-db/dataprep) using bokeh. The original issue is reported here https://github.com/sfu-db/dataprep/issues/179. 

So for one of our functions, plot(df,x), all the graphs in each tab have been shrunk as shown below. 
![image](https://user-images.githubusercontent.com/56427336/84817984-a4b80780-afca-11ea-9335-c4479f40e944.png)
![image](https://user-images.githubusercontent.com/56427336/84818018-aeda0600-afca-11ea-806f-13b1965fe4d0.png)
![image](https://user-images.githubusercontent.com/56427336/84818050-b8fc0480-afca-11ea-9660-bf071d2acaab.png)
We suspected that the legend of the pie chart makes the pie plot small, and for some reason, Bokeh needs to make the same width for the plots in all tabs.

This problem happened with google chrome, IE, Microsoft edge on multiple PC and macOS. However, one of our colleagues who used safari didn't meet this problem on his Macbook.

Changing plot_width and plot_height in Figure() didn't solve this problem. 

The normal plots should be like these:
![image](https://user-images.githubusercontent.com/56427336/84818503-74249d80-afcb-11ea-8a85-586d3723d0c2.png)
![image](https://user-images.githubusercontent.com/56427336/84818511-771f8e00-afcb-11ea-8500-b86b1a770c41.png)




user: dylanzxc
issue_10182: state: open
title: [BUG] Support unsorted/ungrouped labels for nested categorical axes
body: This issues is based on the examples and discussion in https://discourse.bokeh.org/t/misalignment-of-vertical-grey-bars-for-grouped-categorical-axis/5707. Please see that thread for illustrations of the problem.

**Is your feature request related to a problem? Please describe.**

Currently, the grey vertical bar separating nested categories is misaligned unless the nested categories are sorted/grouped according to the outer level. This is unexpected and undocumented as far as I can tell.

**Describe the solution you'd like**

Either update docs to clarify or group the categories internally in bokeh. Note that the order of the categories in the plot will not change with this solution, it will just make sure that the bars are aligned and don't overlap with the axis labels. An example of what could be done would be something like the following:

```python
unique_tuples = df['cat_combo'].unique().tolist()
outer_order = list(dict.fromkeys(x[0] for x in unique_tuples))
grouped_unique_tuples = sorted(unique_tuples, key=lambda x: (outer_order.index(x[0]), x[1]))
```

Which turns the list in `unique_tuples`:

```text
[('B', 'a'),
 ('A', 'a'),
 ('C', 'a'),
 ('B', 'b'),
 ('A', 'b'),
 ('C', 'b'),
 ('B', 'c'),
 ('A', 'c'),
 ('C', 'c')]
```

into this grouped list:

```text
[('B', 'a'),
 ('B', 'b'),
 ('B', 'c'),
 ('A', 'a'),
 ('A', 'b'),
 ('A', 'c'),
 ('C', 'a'),
 ('C', 'b'),
 ('C', 'c')]
```

**Describe alternatives you've considered**
Updated docs as mentioned above.

**Additional context**
See the thread linked above.

user: joelostblom
issue_10171: state: open
title: Improve 3d extension example with echarts
body: This project was recently brought to my attention:

https://echarts.apache.org/examples/en/index.html#chart-type-globe

We could possibly make a better 3d extension example (or even a few different ones) with that that, since vis.js is extremely limited.

cc @philippjfr perhaps it might even make sense to make some built-in/supported extensions at the HV level to connect Bokeh data source to these kinds of things (much more investigation/consideration needed first) 
user: bryevdv
issue_10166: state: open
title: Gallery examples loading all bundles
body: The gallery examples are not pruning the bundles down to the minimum necessary:

<img width="865" alt="Screen Shot 2020-06-13 at 4 34 45 PM" src="https://user-images.githubusercontent.com/1078448/84581230-e9a62900-ad93-11ea-89df-fc3a5c803ad5.png">

user: bryevdv
issue_10160: state: open
title: [FEATURE] Optimize Docs for dyslexia
body: As someone with dyslexia I often feel overloaded by the bokeh docs

I found a couple of references for this. With the one I found most useful being the userzoom blog post. 

https://www.userzoom.com/blog/five-ways-to-make-usable-websites-for-people-with-dyslexia/
https://usabilla.com/blog/how-to-design-for-dyslexia/
https://www.boia.org/blog/how-to-create-accessible-content-and-designs-for-people-with-dyslexia

One thing that stands out as a potential easy win is capping the line length, currently there is no cap and you can end up with line lengths of 165+.


user: shughes-uk
issue_10157: state: open
title: Replacing Iris data with penguin data
body: So given recent events there has been a lot of discussion about the Iris data, which is a standard for demonstrating classification and other statistical techniques. The problem with it is that was first published by Ronald Fisher (a Eugenicist) in the Annals of Eugenics and I don't think I need to point out why that history is extremely problematic for a dataset that has been used for classification problems. It's also incredibly overused and many people will roll their eyes just seeing it.

Thankfully there is a new dataset a lot of people seem to be coalescing around, specifically this open source dataset about penguins: https://github.com/allisonhorst/palmerpenguins, which has several advantages:

* It wasn't popularized by a eugenicist
* It has more interesting dimensions to explore
* It's about penguins 🐧 🐧 🐧 

I therefore propose we replace the Iris data in sampledata and all the associated examples with equivalent examples using the penguin data. Libraries like seaborn have already taken this step and I think we should too.
user: philippjfr
issue_10155: state: open
title: [FEATURE] Derived data sources for selections and viewports
body: A `ColumnDataSource` has selection indices and ongoing work that @mattpap is doing will also allow masking a data source by the current viewport. It would be nice if these masks could be automatically reflected in a derived data source which can be used to generate another plot, which would then automatically reflect the current selection and/or viewport. In combination with a mechanism for expressing column transforms this could be a very powerful way of expressing linked selections entirely on the frontend without having to write custom Javascript.
user: philippjfr
issue_10152: state: open
title: BokehJS API for embedding server sessions
body: User's should be able to embed Bokeh app sessions with a simple JS call, e.g 
```
Bokeh.embed.server_document("< app url>", "<div to embed>")
```
This is certainly already possible in some fashion, but may involve private or undocumented APIs and is not demonstrated anywhere.  


user: bryevdv
issue_10151: state: open
title: Discuss whether to port any components from Panel
body: The [Panel library](https://panel.holoviz.org/) builds on Bokeh and ships with a number of custom bokeh models. Some are very clearly out-of-scope for Bokeh itself but others are potentially interesting to port to Bokeh. In particular we have already discussed porting the [Progress bar model](https://panel.holoviz.org/reference/widgets/Progress.html). But we should discuss more generally if there are other models we want to port, so the easiest thing to do would be to look at the [reference gallery](https://panel.holoviz.org/reference/index.html) and/or the [models module](https://github.com/holoviz/panel/tree/master/panel/models) in Panel and decide if there's anything of interest.
user: philippjfr
issue_10149: state: open
title: [FEATURE] Wrap legend content in Horizontal orientation (or automatic resizing)
body: **Is your feature request related to a problem? Please describe.**
Yes, It's been problemetic for me on how to handle Legends that have a lot of labels (e.g 40 labels), it never shows or wraps content. Currently my legends (40 labels) show cut off, because it's taking the same size of the plot. I moved the legend outside the plot (Above) and still the same issue. 

**Describe the solution you'd like**
It would be great if there was a parameter like wrap=True so horizontal legends wrap on a new line, or to be able to automatically show all legends with the plot size. (

**Describe alternatives you've considered**
Only way this works is to either have a very wide plot or a very long plot with the hight and width paramter, to show all legends. Not sure if there's another way?

**Additional context**
Thanks for the help :) 
user: Vel-San
issue_10139: state: open
title: [FEATURE] Consider removing FontAwesome dependency from the documentation
body: Right now it seems that the only reason to require FontAwesome is to add a single icon to external links to the navbar. If that's the case, then we make users download 77kB just for a single icon.
user: p-himik
issue_10110: state: open
title: Provide easily accessible way of getting debug information in bokehjs
body: Something like:
```ts
Bokeh.info()
```
should give information about:

- loaded versions of bokehjs (all versions)
- active sessions / documents (include bokeh versions)
- model statistics (how many of each type, etc.)
- browser and platform (at least show `navigator.userAgent`)

This will allow to communicate the basic information more easily when reporting bugs, etc.
user: mattpap
issue_10102: state: open
title: TMSTileSource uses XYZ scheme instead of TMS scheme [BUG]
body: Python 3.7.6
Bokeh 2.0.1
HoloViews 1.13.2

I noticed when using `bokeh.models.tiles.TMSTileSource`, that the tiles were misaligned on the y coordinates and were using the XYZ scheme which is also used in the `bokeh.models.tiles.WMTSTileSource` (i.e. the example below had the same output for both `TMSTileSource` and `WMTSTileSource`). Here is a minimal example of that:

```python
import holoviews as hv
import bokeh.models.tiles
hv.extension('bokeh')

URL = 'http://a.tile.osm.org/{Z}/{X}/{Y}.png'
hv.Tiles(bokeh.models.tiles.TMSTileSource(url=URL)).opts(width=600, height=400)
```
This is the output for both the `TMSTileSource` and `WMTSTileSource`:

![Selection_024](https://user-images.githubusercontent.com/9904103/83424367-985d7780-a42c-11ea-8e1e-1c00744b087b.png)

I was unable to find a public data set showing the misalignment. [GeoServer](http://geoserver.org/) can offer TMS and [GDAL2Tiles](https://gdal.org/programs/gdal2tiles.html) creates per default Tiles following the TMS standard. Here is the same example using Leaflet:

```js
var layer = L.tileLayer('http://a.tile.osm.org/{z}/{x}/{y}.png', {
    tms: true, 
    opacity: 0.7, 
    attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors', 
    minZoom: 0, 
    maxZoom: 5
});
```

Here the resulting map for the same URL in Leaflet (as this is not a TMS source):

![Selection_023](https://user-images.githubusercontent.com/9904103/83424044-26852e00-a42c-11ea-8f2e-614e60e04247.png)

Here is the Specification of TMS: https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification
user: njanakiev
issue_10098: state: open
title: [FEATURE] Add helper argument or function to place auto-grouped legends outside the plotting area 
body: As discussed [on discourse](https://discourse.bokeh.org/t/adding-a-legend-outside-the-plot-area-is-possible-even-with-auto-grouped-indirectly-created-legends/5595), it is possible to create automatically grouped legend outside the plot area although the documentation states that it is not:

```python
from bokeh.models import CategoricalColorMapper, Legend
from bokeh.palettes import Category10
from bokeh.plotting import figure, show
from bokeh.sampledata.iris import flowers


color_mapper = CategoricalColorMapper(
    factors=[x for x in flowers['species'].unique()], palette=Category10[10])
p = figure(height=350, width=500)
p.add_layout(Legend(), 'right')
p.circle("petal_length", "petal_width", source=flowers, legend_group='species',
         color=dict(field='species', transform=color_mapper))
show(p)
```

However, this behavior is considered fragile and might cause extra support burden if many users adopt it, so instead of adding an example to the documentation, @bryevdv  suggested the following two solutions:

- Add a helper argument to figure to specify the default legend location (since the figure must be created first this would help ensure the proper ordering of operations)
- Add a helper function to move legends. As is is, legend location is a function of what layout side legend is in, e.g. p.left, p.center. It’s a bit opaque that moving a legend mean moving it from one of those lists to the other. We could make API to simplify that

user: joelostblom
issue_10087: state: open
title: Examples tests report BokehJS version mismatch
body: ```
tests/test_examples.py::test_file_examples[/home/runner/work/bokeh/bokeh/examples/models/file/daylight.py-example11-config11] 
[INFO] Example run in 0.966s
[INFO] Example rendered in 0.873s
[JS] {log} file:///usr/share/miniconda3/envs/bk-test/lib/python3.6/site-packages/bokeh/server/static/js/bokeh.js:7248:21 [bokeh] setting log level to: 'info'
[JS] {warning} file:///usr/share/miniconda3/envs/bk-test/lib/python3.6/site-packages/bokeh/server/static/js/bokeh.js:1104:34 [bokeh] JS/Python version mismatch
[JS] {warning} file:///usr/share/miniconda3/envs/bk-test/lib/python3.6/site-packages/bokeh/server/static/js/bokeh.js:1105:34 [bokeh] Library versions: JS (2.0.2) / Python (40f33cf)
[JS] {info} file:///usr/share/miniconda3/envs/bk-test/lib/python3.6/site-packages/bokeh/server/static/js/bokeh.js:661:34 [bokeh] document idle at 38 ms
PASSED
```
Seems to be using CDN resources, not local ones, which could eventually result in an un-fixable build.

Noting that this was happening before #10058 

cc @mattpap 
user: bryevdv
issue_10081: state: open
title: Sleaker Sliders
body: Our sliders seem *really* overly large and clunky to me. I feel like even a minor tweek could make them substantially more attractive. Below is before/after with some small experimentation in the browser CSS editor:

<img width="322" alt="Screen Shot 2020-05-27 at 5 40 41 PM" src="https://user-images.githubusercontent.com/1078448/83085572-37efc400-a041-11ea-93a8-487c9d52f403.png">
<img width="312" alt="Screen Shot 2020-05-27 at 5 40 18 PM" src="https://user-images.githubusercontent.com/1078448/83085571-36260080-a041-11ea-9e1e-3efa74c1a6ec.png">

Thoughts @bokeh/dev ? @mattpap are there any special considerations to changes like this? AFAIK the CSS changes like this do not/should not affect the layout at all. 

user: bryevdv
issue_10067: state: open
title: [BUG] _line_stack() doesn't accept np.ndarray in stackers argument
body: **Description of problem**
`varea_stack()` and `vline_stack()` process the `stackers` argument differently: the former takes `list`, `np.ndarray`, while the latter only takes `list`. This also holds for `harea_stack()` and `hline_stack()`. 

**Possible solutions**
Adding `stackers=list(stackers)` to `vline_stack()` and `hline_stack()`  in `plotting/figure.py` fixes the issue.

**Additional context**
```
np.random.seed(seed=10)
df = pd.DataFrame(np.random.randint(10, 100, size=(10, 10))).add_prefix('y')
p = figure(
    x_range=(0, len(df)-1),
    y_range=(0, 800),
    height=300, 
    width=300
)
```

1. `varea_stack()` with a `pandas.core.indexes.base.Index`
```
p.varea_stack(
    stackers=df.columns,
    x='index', 
    color=brewer['Spectral'][10], 
    source=df
)
show(p)
```
![image](https://user-images.githubusercontent.com/45575009/82749879-28a01a80-9da4-11ea-98fa-99113da92585.png)

2. `vline_stack()` with a `pandas.core.indexes.base.Index`
```
p.vline_stack(
    stackers=df.columns,
    x='index', 
    color=brewer['Spectral'][10], 
    source=df
)
show(p)
```
```
RuntimeError: 

Expected y to reference fields in the supplied data source.

When a 'source' argument is passed to a glyph method, values that are sequences
(like lists or arrays) must come from references to data columns in the source.
```

3. `varea_stack()` with `list`
```
p.varea_stack(
    stackers=list(df.columns),
    x='index', 
    color=brewer['Spectral'][10], 
    source=df
)
show(p)
```
![image](https://user-images.githubusercontent.com/45575009/82749996-1d99ba00-9da5-11ea-980e-c10bc557b72a.png)


4. `vline_stack()` with `list`
```
p.vline_stack(
    stackers=list(df.columns),
    x='index', 
    color=brewer['Spectral'][10], 
    source=df
)
show(p)
```
![image](https://user-images.githubusercontent.com/45575009/82749991-0bb81700-9da5-11ea-97e5-75b735a0ce3f.png)

user: harabat
issue_10063: state: open
title: [BUG] Custom tileset urls with {s} and {ext} values are not properly parsed
body: **Description of problem**
Base maps can be generated with custom tilesets through `add_tile(XXXXTileLayer(url, attribution))`. Most urls for custom tilesets are of the type `https://tiles.yourtileprovider.come/{z}/{x}/{y}.png`, but many are like `https://{s}.yourtileprovider.com/{z}/{x}/{y}.png` or `https://{s}.yourtileprovider.com/{z}/{x}/{y}.{ext}` (where {s} is the subdomain and {ext} is the format), which breaks `add_tile()`: instead of showing a basemap, it will show an empty grid. The way I had reimplemented this feature in issue #10057, I went with a hacky workaround where:
- I replace {s} by 'a', not sure how hacky it is though, as folium's code seems to be doing the same thing by default, even though subdomains can be specified (the relevant code is in `class TileLayer` in raster_layers.py in folium, if you're interested)
- I replace {ext} by png (I noticed that both jpg and png work). Folium itself is broken by leaving the {ext} though (I'll raise an issue there)

**Possible solutions**
Specifying a url with {s} and {exp} should work straight away. Maybe `add_tile()` could try to replace {s} and {ext} with default values ('a' and 'png') and the docstring could mention something along the lines of `If you specify a url for a custom tileset, it needs to be of the type https://tiles.yourtileprovider.come/{z}/{x}/{y}.png`, so that at least people will know where to look for the problem if their url doesn't work. 

**Alternatives considered**
People could try and find clean urls, but this is not straightforward, as (i) the intricacies of custom tiles are not known by most people who want to work with maps and (ii) most resources, like [OpenStreetMap's list of tile servers](https://wiki.openstreetmap.org/wiki/Tile_servers) and [this list of free tile providers](https://leaflet-extras.github.io/leaflet-providers/preview/), don't consider {s} to be a problem. 

**Additional context**
The description of the subdomains on OSM's tile servers page:
> In the URLS to Carto.com and some others there is also a {s} which should be replaced by the subdomain[1]. Subdomains are used to help with browser parallel requests per domain limitation. These subdomains are typically single letters 'a', 'b' and 'c'. Refer to the provider documentation for details of available subdomains. 

This works:
```
from bokeh.models import WMTSTileSource

p = figure(x_range=(-2000000, 6000000), y_range=(-1000000, 7000000),
           x_axis_type="mercator", y_axis_type="mercator", width=500, height=500)
p.add_tile(
    WMTSTileSource(
        url='https://stamen-tiles-a.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.jpg',
        attribution='Map tiles by <a href="http://stamen.com">Stamen Design</a>, <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a> &mdash; Map data &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
    )
)
show(p)
```
![image](https://user-images.githubusercontent.com/45575009/82737729-028a6400-9d2b-11ea-9580-7249ea496b96.png)

Replacing the clean url above by the original url (`https://stamen-tiles-`**{s}**`.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.`**{ext}** or `https://stamen-tiles-`**{s}**`.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.png`) willl return this:
![image](https://user-images.githubusercontent.com/45575009/82737715-eedefd80-9d2a-11ea-9a7b-5730147c512d.png)

user: harabat
issue_10062: state: open
title: [FEATURE] Allow secondary y-axis to have static range
body: **Is your feature request related to a problem? Please describe.**
Primary and secondary y-axis are always linked together. To plot a candlestick chart with volume bar chart below, the volume bars will move away, if I zoom in.
![candlestick-volume](https://user-images.githubusercontent.com/4254744/82731166-3f436480-9d05-11ea-9175-6f69cc99fc31.png)

**Describe the solution you'd like**
Allow the secondary y-axis to have a static (fixed) range which will not be connected to the primary y-axis. This can be done with a boolean flag. All zoom and pan will only performed for the primary axis.

**Describe alternatives you've considered**
I tried to use a second plot below the candlestick chart, but this takes too much space and looks bad.

**Additional context**
Asked on Stackoverflow.
https://stackoverflow.com/questions/61930843/python-bokeh-plot-static-secondary-y-axis/61934792#61934792
user: debegr92
issue_10056: state: open
title: box_select for line glyphs
body: This issue is to open discussion of implementing box_select for line glyphs. My proposal is to follow the example of [box_select for patches and multi_polygons](https://github.com/bokeh/bokeh/pull/9697) (see also #2325), which does two things: 1) adds a `_hit_rect` to the glyph that selects glyphs that are completely contained by the box_select; and 2) adds a note about the box_select behavior to the glyph documentation.

I have found that copying `_hit_rect` from [box.ts](https://github.com/bokeh/bokeh/blob/master/bokehjs/src/lib/models/glyphs/box.ts) is a simple solution and adequate for this selection behavior:

```
  protected _hit_rect(geometry: RectGeometry): Selection {
    return this._hit_rect_against_index(geometry)
  }
```

While it could be desirable to allow for partial overlap selections, this is a good place to start since it follows the pattern already established by the box, patches, and multi_polygons glyphs.

If this proposal is received favorably I can prepare a PR with the changes.
user: rsprouse
issue_10055: state: open
title: bokeh init -
body: software: bokeh 2.0.2

#### My Pain

I trying to use custom bokeh extensions for awesome-panel.org. I was following along  https://docs.bokeh.org/en/latest/docs/user_guide/extensions.html#pre-built-extensions and running `bokeh init --interactive`. I can see that the package.json file created contains

```js
{
  "name": "awesome-panel",
  "version": "0.0.1",
  "description": "",
  "license": "BSD-3-Clause",
  "keywords": [],
  "repository": {},
  "dependencies": {
    "bokehjs": "^2.0.2"
  },
  "devDependencies": {}
}
```

This is not working. I believe it should be

```js
{
  "name": "awesome-panel",
  "version": "0.0.1",
  "description": "",
  "license": "BSD-3-Clause",
  "keywords": [],
  "repository": {},
  "dependencies": {
    "@bokeh/bokehjs": "^2.0.2"
  },
  "devDependencies": {}
}
```
user: MarcSkovMadsen
issue_10047: state: open
title: Add stacked lines to stacked area plot
body: **Is your feature request related to a problem? Please describe.**
I wonder whether stacked lines could be added to stacked area plots (`vstack_area()`), which is the default in Seaborn or Plotly for example. 

**Describe the solution you'd like**
Stacked line (`vline_stack()`) and area (`vstack_area()`) plots take mostly the same arguments. One solution would be to just call `vline_stack` every time `vstack_area` is called. The solution I wanted to suggest in a PR for discussion is a bit more flexible, as it allows a `line_width` argument to be specified, in which case `vline_stack` is called. 
`bokeh/plotting/figure.py`:
```python
def varea_stack(self, stackers, **kw):
    result = []
    if 'line_width' in kw:
        line_width = kw['line_width']
        kw.pop('line_width')
        result.append(self.vline_stack(stackers=stackers, line_width=line_width, **kw))
    for kw in double_stack(stackers, "y1", "y2", **kw):
        result.append(self.varea(**kw))
    return result
```
instead of 
```python
def varea_stack(self, stackers, **kw):
    result = []
    for kw in double_stack(stackers, "y1", "y2", **kw):
        result.append(self.varea(**kw))
    return result
```
This relies on `vline_stack()` and `vstack_area()` not diverging in future versions however. You will also notice that the legend has changed: it can be fixed if people think it's an issue. 

**Describe alternatives you've considered**
One could just call `vline_stack()` whenever they make a stacked area plot, but other libraries seem to have chosen a different default. 

**Additional context**
Without stacked lines
![Without stacked lines][1]

```python
np.random.seed(seed=10)
df = pd.DataFrame(np.random.randint(10, 100, size=(15, 10))).add_prefix('y')

p = figure(x_range=(0, len(df)-1), y_range=(0, 800))
names = ["y%d" % i for i in range(10)]

p.varea_stack(
    stackers=names, x='index', 
    color=brewer['Spectral'][10], 
    legend_label=names, 
    source=df,
    line_width=2, 
    alpha=0.5,
)
p.legend.items.reverse()

show(p)
```
With stacked lines
![With stacked lines][2]

[1]: https://i.stack.imgur.com/DjVM9.png
[2]: https://i.stack.imgur.com/JlTnX.png
user: harabat
issue_10044: state: open
title: [BUG] Dev resources not working with Django
body: TDB: @philippjfr  or @mattpap fill in details

ref #9924
user: bryevdv
issue_10028: state: open
title: [FEATURE] More default theme for Bokeh
body: During the PyCon Hatchery Mentories sprint, it has been mentioned that currently There's only 3 default theme and it would be great to have more.
user: Cheukting
issue_10023: state: open
title: [BUG] Clearing lines from streamed data using ColumnDataSource fails if data is arrays
body: # Issue
When attempting to clear drawn lines from streamed data via `source.stream(data, rollover=1)` the clear doesn't work if the line's `x` and `y` are numpy arrays in the associated `ColumnDataSource`.  Instead a new line is drawn using the last of the old data and the new data.

This is different to the behaviour if either `x` or `y` is a list.

## Minimal Example
Tested under Windows 10 + Mozilla Firefox in Jupyter Lab with the following packages
```
python=3.7.7
bokeh=2.0.2
numpy=1.18.1
jupyterlab=1.2.6
@bokeh/jupyter_bokeh v1.2.0
```

Actual Example:
```
from bokeh.io import output_notebook, show, push_notebook
from bokeh.models import ColumnDataSource
from bokeh.plotting import figure

import numpy as np

output_notebook()

notebook_url="http://localhost:8888"

p = figure(match_aspect=True)

source1 = ColumnDataSource(data={"x": [-1, 0], "y1": [-1, -1] })
p.line(x='x', y='y1', source=source1, line_width=6, legend_label="List Source", line_color="blue")

source2 = ColumnDataSource(data={"x": np.array([0, 1]), "y1": np.array([0, 0]) })
p.line(x='x', y='y1', source=source2, line_width=6, legend_label="Array Source", line_color="red")

source3 = ColumnDataSource(data={"x": np.array([1, 2]), "y1": [1, 1] })
p.line(x='x', y='y1', source=source3, line_width=6, legend_label="Array X, List Y", line_color="yellow")

source4 = ColumnDataSource(data={"x": [2, 3], "y1": np.array([2, 2]) })
p.line(x='x', y='y1', source=source4, line_width=6, legend_label="List X, Array Y", line_color="orange")

plot_handle = show(p, notebook_url=notebook_url, notebook_handle=True)

### Attempt to clear data
# Update list only source
source1.stream({"x": [4], "y1": [4]}, rollover=1)

# Update array only source
source2.stream({"x": np.array([4]), "y1": np.array([4])}, rollover=1)

# Update array x, list y source
source3.stream({"x": np.array([4]), "y1": [4]}, rollover=1)

# Update array x, list y source
source4.stream({"x": [4], "y1": np.array([4])}, rollover=1)

push_notebook(handle=plot_handle)
```

This raises a Javascript console warning: `[bokeh] data source has columns of inconsistent lengths` caused by the sources with mixed data types. Same behaviour exists when these are removed.

![image](https://user-images.githubusercontent.com/6548259/81940748-d3b81380-95ef-11ea-80c0-65185612fe34.png)
user: Lif3line
issue_10020: state: open
title: [FEATURE] Windowed Auto-ranging.
body: ### FOLLOW THESE INSTRUCTIONS CAREFULLY

*ISSUES THAT DO NOT CONTAIN NECESSARY INFORMATION MAY BE CLOSED, IMMEDIATELY*

The issue tracker is NOT the place for general support. For questions and technical assistance, come join the [Bokeh Project Discourse](https://discourse.bokeh.org).

**Is your feature request related to a problem? Please describe.**
I have been working on implement an auto-ranging solution for my Bokeh based dash-board.

It is usually a good practice to have the plot nicely fit on the preferred axis (in my case it was the `y-axis`). In that way, the value ranges in the current window, which is a slice of the data after a drag and a zoom interaction, are efficiently fit and displayed proportionally. I believe this improves the usability, together with the readability of the plot.

**Describe the solution you'd like**
I would expect the values to be visually fit within their respective `min/max` ranges and displayed adaptively as we drag and zoom inside the Bokeh canvas.

**Describe alternatives you've considered**
While I was trying to implement this solution, I came across many "hacks" aimed at achieving this result. I think it would be a good idea to possibly provide a built-in, native approach for solving this problem.

**Additional context**
Right now, this is achieved though `js_on_change` callbacks that control the `y-range` or the `x-range` depending on your plot. Here is the result achieved after some callback hacking.

![candlesticks6](https://user-images.githubusercontent.com/48017279/81923020-ebce6980-95d4-11ea-884c-12cd0eaa22f3.gif)

I have implemented the following hack:
[Python: Plot candlesticks with automatic Y zoom](https://stackoverflow.com/questions/51162010/python-plot-candlesticks-with-automatic-y-zoom
)

Besides the fact that many of these hacks are outdated, thus the constant need for fixing and adapting so they can work for the newer Bokeh versions, the overall experience introduces a lot of friction.

In my case, the reset of the `y-range` was fixed through the use of the `Range1d` range type. Below is a link to the Bokeh Community Support post.

[Dropdown Menu Event resetting Y-size on the second plot](https://discourse.bokeh.org/t/dropdown-menu-event-resetting-y-size-on-the-second-plot/5442/15)

I hope eventually there will be a more streamlined and standardised way to handle the "windowed auto-range".
user: mbilyanov
issue_10017: state: open
title: Switch server default resources to CDN?
body: @bokeh/dev I would like to consider changing the default resources for Bokeh server to CDN. For almost all real user situations I can think of this represents an improvement. Viewers of apps will get to take advantage of CDN caching and apps will not have to waste compute and bandwidth on serving resources themselves. 

 Local dev will be slightly complicated by needing to specify to use local resources. 

Thoughts? I would say this could happen for 2.1 personally. 
user: bryevdv
issue_10010: state: open
title: [FEATURE] Improve Mapping Geo Data documentation
body: Hello,

The [Users Guide documentation on Mapping Geo Data](https://docs.bokeh.org/en/latest/docs/user_guide/geo.html) seems to under-represent the Bokeh GIS mapping capability. Bokeh has at least 10 open source map tile providers but there is no mention of them individually in the Users Guide.

A display of all map providers with their different styles seems appropriate and is surprisingly absent.

Also, Google Maps has recently increased the complexity in getting their mapping API working. Now it requires an API key and also a google registration that is not straightforward. This is a substantial barrier to open-source projects that rely on Bokeh for mapping.

Bokeh provides open-source equivalents, for example OpenStreetMaps and ESRI satellite image tile providers that make mapping with open-source maps and satellite imagery easily available.

How can the Users Guide page be updated to reflect the different tile providers and illustrate Bokeh's full open-source mapping capability?

Marc

Reference:
1. this feature request is an issue from [this Discourse conversation](https://discourse.bokeh.org/t/coordinate-conversions-between-lat-lon-and-web-mercator/5356)


user: comperem
issue_10009: state: open
title: [FEATURE] add built in coordinate conversions
body: Hello,

The GIS and mapping capability of Bokeh is good and growing but lacks ability to convert units between (lat,lon) pairs and Web Mercator Easting and Northing coordinate pairs.

[`pyproj`](https://pypi.org/project/pyproj/) is a simple, focused and mature coordinate conversion utility for converting between recognized standard coordinate frames.

Adding `pyproj` or a similar capability would allow native Bokeh code and examples to perform coordinate conversions as a normal part of GIS mapping and plotting. For example, this code and associated documentation example could be included: 
![bokeh_tiles_example](https://user-images.githubusercontent.com/29280226/81613631-d1b24280-93ac-11ea-982e-cbadf183b974.jpg)


I propose adding `pyproj`. How can this get considered for the next version update?

Alternative codes and methods:
1. [OpenStreetMap Mercator wiki](https://wiki.openstreetmap.org/wiki/Mercator) with conversions written in multiple programming languages
2. [Web Mercator projection wiki](https://en.m.wikipedia.org/wiki/Web_Mercator_projection) has formulas and good explanations

References:
1. [discourse link located here](https://discourse.bokeh.org/t/coordinate-conversions-between-lat-lon-and-web-mercator/5356).
2. [pull request with working example](https://github.com/bokeh/bokeh/pull/9710) except for `pyproj` dependency.




user: comperem
issue_9997: state: open
title: Cache invalidation doesn't work in `node make build`
body: Have tried all of the following tp get unstuck, to no avail:

* `npm cache clear --force`
* `rm -rf node_modules`
* updating to nodejs 12.x

Also tried manually installing `esquery` after all that. Always get:
```
[19:10:02] Starting 'compiler:build'...
[19:10:03] Failed 'compiler:build' after 521 ms
[19:10:03] Error: '/Users/bryan/work/bokeh/bokehjs/node_modules/esquery/esquery.js doesn't exist
[19:10:03]     at Linker.new_module (/Users/bryan/work/bokeh/bokehjs/src/compiler/linker.ts:552:13)
[19:10:03]     at Linker.resolve (/Users/bryan/work/bokeh/bokehjs/src/compiler/linker.ts:711:27)
[19:10:03]     at Linker.link (/Users/bryan/work/bokeh/bokehjs/src/compiler/linker.ts:267:28)
[19:10:03]     at Task.fn (/Users/bryan/work/bokeh/bokehjs/make/tasks/compiler.ts:31:27)
[19:10:03]     at exec_task (/Users/bryan/work/bokeh/bokehjs/make/task.ts:147:34)
[19:10:03]     at _run (/Users/bryan/work/bokeh/bokehjs/make/task.ts:176:24)
[19:10:03]     at _run (/Users/bryan/work/bokeh/bokehjs/make/task.ts:168:26)
[19:10:03]     at _run (/Users/bryan/work/bokeh/bokehjs/make/task.ts:168:26)
[19:10:03]     at Object.run (/Users/bryan/work/bokeh/bokehjs/make/task.ts:192:22)
[19:10:03]     at main (/Users/bryan/work/bokeh/bokehjs/make/main.ts:18:16)
```


user: bryevdv
issue_9995: state: open
title: [BUG] Updating CDS with CustomJS and PointDrawTool Run Very Slow in Microsoft Edge
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
Python 3.7.6 (default, Jan  8 2020, 20:23:39) [MSC v.1916 64 bit (AMD64)] :: Anaconda, Inc. on win32
Bokeh version : 2.0.2, 1.4, 1.2
Microsoft Edge 44.17763.831.0
Chrome Version 81.0.4044.113
Firefox v75.0 (64-bit)

#### Description of expected behavior and the observed behavior
Updating ColumnDataSource and DataTable with js_on_change CustomJS and PointDrawTool run very slow in MS Edge compared to other browsers. Below code sort the source data after editing data points with PointDrawTool. It runs 10x slower in Edge compared to Chrome and Firefox. Running the code when Edge Console is opened slow it 40x times. Without the DataTable, the code runs considerably faster but still 3x slower than other browsers.

#### Complete, minimal, self-contained example code that reproduces the issue

```
from bokeh.plotting import Column, ColumnDataSource, figure, output_file, show, Row
from bokeh.models import DataTable, TableColumn, PointDrawTool, ColumnDataSource, CustomJS
import numpy as np

# Create Random data
x=np.random.rand(1000,1)
y=np.random.rand(1000,1)
data = {'x': x,
        'y': y,
       'color': ['blue']*len(x)}

source = ColumnDataSource(data=data)

# Setup Figure
p = figure(plot_width=700, plot_height=500)

p.line('x', 'y', line_width=2, source=source)
rend = p.scatter(x='x', y='y', source=source, color='color', size=10)

# Setup Table
columns = [TableColumn(field="x", title="x"),
           TableColumn(field="y", title="y"),
          TableColumn(field='color', title='color')]
table = DataTable(source=source, columns=columns, editable=True, height=200, width=700)

# Run Time Table
source2 = ColumnDataSource(data = dict(t = []))
columns2 = [TableColumn(field="t", title="Sorting Execution Time [ms]")]
table2 = DataTable(source=source2, columns=columns2, editable=False, height=100, width=200)

# Draw Tool
draw_tool = PointDrawTool(renderers=[rend], empty_value='red')
p.add_tools(draw_tool)
p.toolbar.active_tap = draw_tool

# Sort Data after Draw Tool
source.js_on_change('data', CustomJS(args = dict(s1 = source, s2 = source2,  table=table2), code="""\
let t0 = performance.now()
let prev_x = null;
let sorted = true;
for (const x of s1.data.x) {
    if (prev_x !== null && prev_x > x) {
        sorted = false;
        break;
    }
    prev_x = x;
}
if (!sorted) {
    const acc = (s1.data.x
                 .map((x, idx) => [x, s1.data.y[idx], s1.data.color[idx]])
                 .sort(([x1], [x2]) => x1 - x2));
    s1.data.x = acc.map(([x]) => x);
    s1.data.y = acc.map(([, y]) => y);
    s1.data.color= acc.map(([, , color]) => color);
    s1.change.emit();
}
        
    let dt = performance.now()-t0;
    dt = +dt.toFixed(2);
    s2.data.t[0]=dt;
    s2.change.emit();
    table.change.emit();
    console.log("Finished in [" + dt + "] milliseconds.")
"""))

# Output File
output_file("drawtool.html")
show(Row(Column(p, table), table2))

```

#### Stack traceback and/or browser JavaScript console output

```

Edge Speed:  [250] milliseconds.
Edge Speed with Console opened:  [800] milliseconds.
Edge Speed without DataTable:  [70] milliseconds.
Edge Speed without DataTable and with Console opened:  [110] milliseconds.

Chrome Speed:  [15] milliseconds.
Firefox Speed:  [25] milliseconds.

```
user: ys4503
issue_9983: state: open
title: [BUG] Can't change jitter width with a CustomJS callback
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)

bokeh 2.0.2
python 3.8.2
macOS
Google Chrome Version 81.0.4044.129 (Official Build) (64-bit)

#### Description of expected behavior and the observed behavior

*Expected behavior*: The jitter width is supposed to be linked to a slider value in a standalone document with `slider.js_link("value", jitter, "width")` or a simple CustomJS callback.

*Observed behavior*: The jitter width does not change with the slider value.

#### Complete, minimal, self-contained example code that reproduces the issue

Find below a minimal example with the `js_link` and 3 `CustomJS` alternatives that does not work.

```python
import bokeh.layouts as bl
import bokeh.models as bm
import bokeh.plotting as bp
import bokeh.transform as bt
from bokeh.sampledata.iris import flowers

x = "petal_length"
y = "species"

source = bm.ColumnDataSource(flowers)
y_factors = sorted(flowers[y].unique())
y_range = bm.FactorRange(factors=y_factors)

slider = bm.Slider(title="Jitter width", value=0.5, start=0, end=1, step=0.1)
jitter = bm.Jitter(width=slider.value, range=y_range)

p = bp.figure(y_range=y_range)
r = p.circle(x=x, y=bt.transform(y, jitter), size=8, source=source,)
p.xaxis[0].axis_label = x
p.yaxis[0].axis_label = y

dashboard = bl.row(p, slider)

# slider.js_link("value", jitter, "width")


# callback_code = """
# r.glyph.y.transform.width = cb_obj.value;
# r.glyph.change.emit();
# """
# callback = bm.CustomJS(args={"r": r}, code=callback_code)
# slider.js_on_change("value", callback)


# callback_code = """
# jitter.width = width;
# r.glyph.change.emit();
# """
# callback = bm.CustomJS(args={"jitter": jitter, "r": r}, code=callback_code)
# slider.js_on_change("value", callback)


callback_code = """
jitter.width = cb_obj.value;
source.change.emit();
"""
callback = bm.CustomJS(args={"jitter": jitter, "source": source}, code=callback_code)
slider.js_on_change("value", callback)

bp.show(dashboard)
```

#### Stack traceback and/or browser JavaScript console output

#### Screenshots or screencasts of the bug in action

<img width="919" alt="Screenshot 2020-05-02 at 12 18 18 PM" src="https://user-images.githubusercontent.com/6806332/80861477-17fdf800-8c6f-11ea-819e-b00aef50b05f.png">
<img width="919" alt="Screenshot 2020-05-02 at 12 18 26 PM" src="https://user-images.githubusercontent.com/6806332/80861478-1af8e880-8c6f-11ea-9626-8cd0d7deae66.png">

user: lgiraldi
issue_9980: state: open
title: Migrate examples/integration to bokehjs' integration tests
body: 

user: mattpap
issue_9979: state: open
title: Add a script for fetching bokehjs-report artifacts
body: This should be easily possible with GitHub's API, but currently doesn't work, at least for me. Maybe the reason is that the involved APIs have experimental status.

My setup:
```sh
alias gh="curl -s -u $GITHUB_ACCESS -H 'Accept: application/vnd.github.antiope-preview+json'"
```
Where `$GITHUB_ACCESS` is your `username:token`. The token has to have `public_repo` scope enabled to allow you to access artifacts. The `Accept` header is there to allow experimental APIs.

To get a list of checks that runned for a commit (`HEAD` in this case), issue:
```sh
gh https://api.github.com/repos/bokeh/bokeh/commits/$(git rev-parse HEAD)/check-runs
```
This will give you names and IDs of check runs to filter out, e.g. you can pipe the output to jq:
```sh
| jq -C '.check_runs[] | .id,.name' | less -r
```
The get a list of artifacts of a check run, issue:
```sh
gh -i https://api.github.com/repos/bokeh/bokeh/actions/runs/635878658/artifacts
```
Replace the ID with an actual one obtained in the previous step. This is the part that doesn't work, resulting in:
```js
{
  "message": "Not Found",
  "documentation_url": "https://developer.github.com/v3/actions/artifacts/#list-workflow-run-artifacts"
}
```
You can obtain a list of all artifacts with:
```sh
gh -i https://api.github.com/repos/bokeh/bokeh/actions/artifacts
```
This unfortunately doesn't give any references to PRs, check runs, etc. To obtain a download link, issue:
```sh
gh -i https://api.github.com/repos/bokeh/bokeh/actions/artifacts/5443284/zip
```
Once again, replace the ID with an actual artifact ID. This will give you a direct download link in `Location` response header, approximately like this:
```
Location: https://pipelines.actions.githubusercontent.com/<<LONG_URL>>
```
This can finally be downloaded with:
```sh
curl -O artifact_name.zip $ARTIFACT_LOCATION
```

user: mattpap
issue_11096: state: open
title: Implement throttling/debouncing on BokehModel
body: After having worked with the `BokehModel` a fair bit I still haven't found a good way to implement throttling/debouncing since events continue accumulating while a previous event is processing. It would therefore be nice to implement throttling/debouncing right on the BokehModel.
user: philippjfr
issue_9962: state: open
title: Include example code of PolyEditTool and PolyDrawTool Docs 
body: Currently the page https://docs.bokeh.org/en/latest/docs/user_guide/tools.html shows gif images of how to use the tools (which are great!). 

However, they don't include the code on how to use these tools in practice.

It took me an embarrassingly long time work workout how to use the PolyEditTool.

It would be great if the docs also included sample code on how to use the tools as well as the gif image.

These examples already exist in ```bokeh/sphinx/source/docs/user_guide/examples/tools_poly_draw.py```
and 
```bokeh/sphinx/source/docs/user_guide/examples/tools_poly_edit.py```

Could the code in these examples be linked directly to  https://docs.bokeh.org/en/latest/docs/user_guide/tools.html

user: scaine1
issue_9946: state: open
title: [FEATURE] support regexes as URL paths in Django
body: **Is your feature request related to a problem? Please describe.**
Django server integration only supports fixed-string URL paths in `myproject.urls.bokeh_apps`. This makes it impossible to customize those apps based on URL arguments - I'd like to construct app instances using database objects referenced in URL slugs. 

**Describe the solution you'd like**
Support regexes in `bokeh_apps` as URL patterns.

**Describe alternatives you've considered**
Bypassing `bokeh_apps` entirely and constructing channel's routing by hand everywhere I need it. This would be sad.

Similar behaviour was requested in bokeh/bokeh-django#4.

**Additional context**
PR forthcoming. Also tweaks `bokeh.server.contexts._RequestProxy` to better support Django-style request dictionaries.
user: PeteFein
issue_9943: state: open
title: [FEATURE] Better numeric input widget
body: Numeric inputs are one of the most common and fundamental forms of user interaction in a dashboard or application.  Bokeh offers Slider, Spinner, and TextInput widgets that allow  specifying numeric values, but none of them cover the full range of what users want to do with numeric values. Specifically, in different circumstances people want to:

1. Enter a very specific, precise value like 23.649, as is supported by a TextInput.
2. Step a value up and down with a certain precision, as is supported by a Spinner and in some cases by a Slider.
3. When the total number of values is small enough (e.g. an integer from 1 to 50), scan easily to any point in the range with immediate feedback, as is supported by a Slider.
4. For a larger but still bounded range (e.g. an integer from 0 to 1 million or a float from 0 to 10), scan easily to any point in the allowed range (but limited by a certain precision) with immediate feedback, as is also supported by a Slider.
5. For an unbounded range or a very large range where "typical" values are in a small range (e.g. floats 0 to 10) but allowed values are in a very large range (e.g. floats 0 to 1 million or 0 to infinity), scan easily to any point in the typical range with decent precision (supported by a Slider) while still being able to input any allowable value (not currently supported at all).

As listed here, Bokeh already supports nearly all of these usages well apart from usage 5. I think usage 5 is a very common or perhaps the most common case; many (most?) physical quantities have a meaningful lower bound but no absolute upper bound (mass, temperature, weight, length, volume, energy, cost, etc., etc.). The lack of good support for case 5 is my most pressing concern, but it's also frustrating that the developer has to choose which of the other usages is the most relevant for a particular situation, prioritizing precision by selecting a TextInput, small stepping by selecting a Spinner, easy ranging by selecting a Slider, etc., but not easily being able to support multiple such usages. _Sometimes_ a developer can easily and correctly make such a decision, but often it's the _user_ who wants to make that call.  _Some_ users in _some_ situations want to enter a precise value, and _other_ users in _other_ situations want to quickly try out a bunch of values, which really isn't up to the developer to decide.

Given the above, I would love if Bokeh could deal with this problem once and for all by providing a numeric input widget that handles all of these usages and lets the _user_ decide at any instant what their priority is and how they want to specify a value at that moment. Specifically, I think the following single widget would cover nearly every case I want to get a number from the user, while letting the user choose which mode of interaction they want:

- A slider covering a certain range. This range would be specified as the smaller of a soft bounds range and a hard bounds range, where only one of those ranges needs to be supplied. The hard bounds range is absolute; the widget cannot be set outside this range.  The soft bounds specifies the initial range of the slider, but the slider range can expand (see below) to any range up to the hard bounds range (if any).
- A text input field allowing any number to be written in. If the number is outside the hard range, it is disallowed, the UI indicates an error, and the previous (presumably allowable) value is kept. If the new (allowed) value is outside the lower or upper range of the slider's current range, the upper or lower end of the slider range is updated to include it, potentially with a corresponding loss in precision of the slider.
- Possibly a spinner widget next to the text input field allowing stepping using the current precision level. Stepping in this way is something I rarely do, so this is less of a priority for me personally, but presumably some people want to do this, and putting the spinner arrows would let them do this. I believe it's also supported already by Sliders using keyboard shortcuts, which again makes this less of a priority for me, but I'm including it for completeness.

I think this widget would cover what people want to do with numeric inputs in nearly all cases. It doesn't cover _everything_, of course, as there is e.g. no way to set a precision explicitly in the UI (useful for indicating how much to step by), nor any way to reduce the range of values once it gets expanded.  But I think those are some obscure cases, and I think trying to add UI for them would just make the component be complex and confusing, so I would argue against including functionality like that. In contrast, the combination of a slider, numeric text input, and spinner arrows should be immediately intuitive to users and allow them to do _nearly_ anything they want to do with numeric inputs, so I would love to see a widget like this appear in Bokeh.

Related issues: https://github.com/holoviz/panel/issues/1128, https://github.com/jupyter-widgets/ipywidgets/issues/1171
user: jbednar
issue_9932: state: open
title: [FEATURE] Get back element focus after selection
body: This request is linked [to this question](https://discourse.bokeh.org/t/select-datatable-row-using-text-input-with-selection/5202).

**Issue:** When you manually change the datatable source selection, you lost focus on the current element (or at least using InputText). 

**Example:** Here is an example where I tried to make a search tool using an input text, and for which I have to redefined the selected rows. Whenever you make a search in which at least one selected element is found, the InputText focus is lost (after setting a new attribute to `source.selected.indices`).

Adding a customJS to the same event does nothing, I guess it's processed before the table change.

[Maybe this other example is linked to the same issue](https://stackoverflow.com/questions/58202339/python-bokeh-how-to-set-focus-back-to-textinput-after-layout-change).

Bokeh : 2.0.0
Python : 3.7.6 (anaconda)
MacOS HiSi

```python3
from bokeh.io import curdoc
from bokeh.layouts import column
from bokeh.models import ColumnDataSource, CustomJS
from bokeh.models.widgets import TextInput, DataTable, TableColumn
import pandas as pd

class SelectableDataTable() :

    def __init__(self, df, select_column) :
        self.df = df
        self.sdf = df
        self.source = ColumnDataSource(df)

        self.select_column = select_column

        self.ti = TextInput(name="TIName")
        self.ti.on_change("value_input", self.input_callback)

        columns = [TableColumn(field=column, title=column) for column in df.columns]
        self.dt = DataTable(source=self.source, columns=columns, selectable="checkbox")

        self.source.selected.on_change("indices", self.source_selection_change)
        self.save_selection = True
        self.selected = set()

        code = """
        document.getElementsByName(obj.name)[0].focus();
        console.log('make focus');
        """

        custom = CustomJS(args=dict(obj=self.ti), code=code)
        self.source.selected.js_on_change("indices", custom)

    def input_callback(self, attr, old, new) :
        self.sdf = df[df[self.select_column].str.contains(new)]
        self.source.data = self.sdf

        sdf_idxs = list(self.sdf.index)
        select_idx = [idx for idx, sdf_idx in enumerate(sdf_idxs) if sdf_idx in self.selected]

        self.save_selection = False
        self.source.selected.indices = select_idx
        self.save_selection = True

    def source_selection_change(self, attr, old, new) :
        if not self.save_selection : return
        sdf_idxs = list(self.sdf.index)
        selected = {sdf_idxs[index] for index in new}
        unselected = set(sdf_idxs) - selected
        self.selected = (self.selected | selected) - unselected

df = pd.DataFrame({'numbers': list(range(10)), 'colors': ["red", "blue"] * 5}, columns=['numbers', 'colors'])
sdt = SelectableDataTable(df, "colors")

layout = column(sdt.ti, sdt.dt)
curdoc().add_root(layout)
```
user: jsgounot
issue_9930: state: open
title: [FEATURE] Add ability to encrypt token
body: Currently we serialize cookies as part of the JWT token which allows it to be accessed on all processes. The problem with this is that we are potentially leaking secret information this way because even secret cookies are included (and currently are by default). We should either ship a mechanism to encrypt the entire token or specific values in the token so that it cannot be decoded when embedded in the template.

The two options we have here as far as I see it:

* Provide some encryption mechanism directly in bokeh
* Make it easier to encrypt and decrypt in the request hooks (technically this is already possible but maybe there's something else we can do)

Additionally we should consider defining a default whitelist of cookies and/or explicitly exclude secret cookies from the default includes to ensure that we do not leak information by default.
user: philippjfr
issue_9923: state: open
title: Does AutocompleteInput have a "find inside the text" option instead of "beggining with"? 
body: For example if we type "mask" and completions contain "The mask" then it would match and display the completion.  
user: gmanolia
issue_9917: state: open
title: [BUG] Example For 'DateTime Axes' On user_guide/quickstart.html needs modification
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
Bokeh: `2.0.1`
Python: `3.8.1`
Notebook: `6.0.2`, though I'm running the file on a terminal
OS: `Linux archlinux 5.5.3-arch1-1 #1 SMP PREEMPT Tue, 11 Feb 2020 15:35:41 +0000 x86_64 GNU/Linux`

#### Description of expected behavior and the observed behavior

Example for `datetime axes` on the page `user_guide/quickstart.html`, [here](https://docs.bokeh.org/en/latest/docs/user_guide/quickstart.html), does not work correctly. 
Line 3, which is,
```
from bokeh.sampledata.stocks import AAPL
```
runs into a problem when ran with `python datetime_axes.py`. where it complains that it cannot find the sample data on the machine. It also provides the immediate solution, which is to just use the method, `bokeh.sampledata.download()` to download the sample data locally first. 

Including the below code,
```
import bokeh
bokeh.sampledata.download()
```
at the top of the file **immediately** fixes this issue,  though nothing like this is mentioned in the `quickstart.html` page.

#### Complete, minimal, self-contained example code that reproduces the issue

A fresh install of bokeh using `pip install bokeh`,

And running the code for `datetime axes` on the page `user_guide/quickstart.html`, [here](https://docs.bokeh.org/en/latest/docs/user_guide/quickstart.html), or saving you a click, run the below code
```
import numpy as np

from bokeh.plotting import figure, output_file, show
from bokeh.sampledata.stocks import AAPL

# prepare some data
aapl = np.array(AAPL['adj_close'])
aapl_dates = np.array(AAPL['date'], dtype=np.datetime64)

window_size = 30
window = np.ones(window_size)/float(window_size)
aapl_avg = np.convolve(aapl, window, 'same')

# output to static HTML file
output_file("stocks.html", title="stocks.py example")

# create a new plot with a datetime axis type
p = figure(plot_width=800, plot_height=350, x_axis_type="datetime")

# add renderers
p.circle(aapl_dates, aapl, size=4, color='darkgrey', alpha=0.2, legend_label='close')
p.line(aapl_dates, aapl_avg, color='navy', legend_label='avg')

# NEW: customize by setting attributes
p.title.text = "AAPL One-Month Average"
p.legend.location = "top_left"
p.grid.grid_line_alpha = 0
p.xaxis.axis_label = 'Date'
p.yaxis.axis_label = 'Price'
p.ygrid.band_fill_color = "olive"
p.ygrid.band_fill_alpha = 0.1

# show the results
show(p)
```

Using `python datetime_axes.py`.

#### Stack traceback and/or browser JavaScript console output

Not related to JS - it's just Python.

#### Screenshots or screencasts of the bug in action

![image](https://user-images.githubusercontent.com/41635766/79051320-006fb880-7c49-11ea-8c72-eafe6d32dc5d.png)

Thank you, and have a nice day!
Would like to make a pull request to just edit in the two lines myself ( or just a note before the code ), but I was expecting a folder called `docs`, or `web` which is most commonly used to contain the documentation, so I don't know exactly where to make the modification.
user: Rubix982
issue_9912: state: open
title: Triggering JS callback when activating/deactivating a tool
body: After a discussion in [the discourse forum](https://discourse.bokeh.org/t/triggering-js-callback-when-activating-deactivating-a-tool/5151), I would like to suggest to make it possible to  trigger callback events upon activating/deactivating plot tools. 

I think it might be of broader interest. 
user: drdqde
issue_9911: state: open
title: Trigger JS callback on data table edit
body: Bases on [this](https://discourse.bokeh.org/t/trigger-js-callback-on-data-table-edit/5150/2), it might be good to check how it should work to trigger a (js) callback upon changing data in a DataTable. 

Currently, `data_source.js_on_event('patching', callback)` does the job. But it might feel more natural to have `data_source.js_on_event('patching', callback)` or data_table.js_on_event('edit', callback)` triggering events. 
user: drdqde
issue_9887: state: open
title: [BUG] DataTable StringEditor autocomplete not working
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
bokeh: 2.0.1
python: 3.7.7
OS: Mojave (10.14.6)
Browsers: Chrome, Firefox, Safari

#### Description of expected behavior and the observed behavior
Expected: TableColumn that was defined with a StringEditor with completions set should provide autocomplete functionalities when editing in browser. 

Observed: Autocomplete suggestions are not shown while editing.

#### Complete, minimal, self-contained example code that reproduces the issue
```
from bokeh.models import ColumnDataSource
from bokeh.models.widgets.tables import (
    DataTable, TableColumn, IntEditor, SelectEditor, StringEditor
)
from bokeh.io import curdoc

dict1 = {
    'x': [0, 0, 0, 0, 0, 0],
    'y': [0, 1, 0, 1, 0, 1],
    'str': ['one', 'two', 'three'] * 2,
    'str2': ['one', 'two', 'three'] * 2,
}
source = ColumnDataSource(data=dict1)

columns = [
    TableColumn(field="x", title="x"),
    TableColumn(field="y", title="y", editor=IntEditor(step=1)),
    TableColumn(field="str", title="str", editor=StringEditor(completions=["one", "two", "three", "four"])),
]

data_table = DataTable(
    source=source,
    columns=columns,
    width=800,
    editable=True,
    reorderable=False,
)

curdoc().add_root(data_table)
```

#### My observations
It seems that this functionality was commented out here
https://github.com/bokeh/bokeh/blame/2.0.1/bokehjs/src/lib/models/widgets/tables/cell_editors.ts#L155

in this huge commit: 
https://github.com/bokeh/bokeh/commit/75460498f061aa1ae1b1f4aab2a27eb3890782f6

Unfortunately it is not clear from the commit message why these lines where commented out. Does it have to do with removal of jquery? I see most of those lines make use of `$`.

#### Plans for support vs Documentation
A big problem is that this functionality is still documented as if it was working. We were comparing a few prototyping frameworks to use in a client project and were planning to use the Bokeh because of those features. We tested them and now are considering other options but still Bokeh seems to be the best choice.

So we could allocate some time to fix those issues but would need some guidance. 

Seems the problematic commit came from @mattpap. Do you still remember why the comments? What needs to be done to enable this features again?

user: kayibal
issue_9879: state: open
title: [BUG] Impossible to change FactorRange to a lower dimension with different number of factors
body: Initially reported at https://discourse.bokeh.org/t/y-ticks-y-range-error-while-changing-from-2-dimensional-x-range-factors-to-1-dimensional/5106

#### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
Bokeh 2.0.1

#### Description of expected behavior and the observed behavior
It should be possible to switch between `factors` of a `FactorRange` without any issues.

#### Complete, minimal, self-contained example code that reproduces the issue

```python
from bokeh.io import show
from bokeh.layouts import column
from bokeh.models import Button, CustomJS, ColumnDataSource, FactorRange
from bokeh.plotting import figure

fig = figure(x_range=FactorRange(factors=[('a', 'b'), ('b', 'c')]))

ds = ColumnDataSource(data=dict(x=[['a', 'b'], ['b', 'c']], y=[1, 2]))
fig.vbar(x='x', top='y', source=ds)

b = Button(label="Switch to single level")

b.js_on_click(CustomJS(args=dict(fig=fig, ds=ds),
                       code="""\
                           fig.x_range.factors = ['a'];
                           ds.data = {x: ['a'], y: [1]};
                           ds.change.emit();
                       """))

show(column(b, fig))
```

At least one of the causes is that `FactorRange.tops` is not updated if we switch to 1D factors from 2D or 3D factors: https://github.com/bokeh/bokeh/blob/cd74b12ed245c3d53c8bf21420de0f14979aebd0/bokehjs/src/lib/models/ranges/factor_range.ts#L240
There seems to be at least one other issue because the error that you see may depend on the order of the instructions within the `CustomJS.code` above. Seems like a race condition.
user: p-himik
issue_9878: state: open
title: Can legend_group be linked to CDSView filtering?
body: **Issue:**
Having an issue when I filter data with CDSView in bokeh. The label in the plot shows all labels in the source, but is it possible to have it show only filtered labels. 

**Example:**
In the example below the CDSView is filtered on "lo" but instead of it showing legend only for "lo", its showing both groups in and with the same color. 

```
from bokeh.io import show
from bokeh.models import ColumnDataSource, GroupFilter, CDSView
from bokeh.palettes import RdBu3
from bokeh.plotting import figure

c1 = RdBu3[2] # red
c2 = RdBu3[0] # blue
source = ColumnDataSource(dict(
    x=[1, 2, 3, 4, 5, 6],
    y=[2, 1, 2, 1, 2, 1],
    color=[c1, c2, c1, c2, c1, c2],
    label=['hi', 'lo', 'hi', 'lo', 'hi', 'lo']
))

p = figure(x_range=(0, 7), y_range=(0, 3), plot_height=300, tools='save')

# legend field matches the column in the source
p.circle( x='x', y='y', radius=0.5, color='color', legend_group='label', source=source, view = CDSView(source=source, filters=[GroupFilter(column_name='label', group='lo')]))

show(p)
user: zanapepaj
issue_9840: state: open
title: Add a test for #9835
body: Preliminary code for https://github.com/bokeh/bokeh/blob/master/tests/integration/widgets/test_datepicker.py:
```python
    @flaky(max_runs=10)
    def test_server_value_change(self, bokeh_server_page) -> None:
        new_date = '2019-9-21'

        def modify_doc(doc):
            dp = DatePicker(value=date(2019, 9, 20), min_date=date(2019, 9, 1), max_date=date(2019, 9, 30))
            b = Button(label='Set date via server', css_classes=['foo'])

            def set_date():
                dp.value = new_date

            b.on_click(set_date)
            doc.add_root(column(dp, b))

        page = bokeh_server_page(modify_doc)

        button_el = page.driver.find_element_by_css_selector('.foo button')
        button_el.click()

        el = page.driver.find_element_by_css_selector('.bk-input')
        assert el.get_attribute('value') == new_date
```

#9835 (just to link to the PR since the ID in the title doesn't work)
user: p-himik
issue_9792: state: open
title: Rich Previews for Docs
body: The Bokeh Documentation (and other properties) could benefit from the addition of metadata such a [OpenGraph](https://ogp.me/) and [Structured Data](https://developers.google.com/search/docs/guides/intro-structured-data). Adding these would afford rich previews on many common social media sites, forums, and Google search results. The task is to first add metadata manually to small sites such as [bokeh.org](https://bokeh.org) and then develop Sphinx extensions to automate adding relevant metadata throughout the entire docs site [docs.bokeh.org](https://docs.bokeh.org).

user: bryevdv
issue_9788: state: open
title: Compression of data embedded in html/notebooks
body: I found that using Bokeh via hvplot with embedding data into the notebook (`dynamic=False` option) is a good way to share interactive plots with people. However the files quickly get very large, especially with gridded (image) plots - if you have a few dimensions there are a lot of images and the file can quickly get >100mb. But they do compress very well with zip (I guess because it's image data). This suggests the data could be compressed better within the browser, or maybe there could be more options for different compression methods.

user: mangecoeur
issue_9774: state: open
title: [FEATURE] Dynamically positioned stacked bar chart segments
body: One feature in bokeh allows a user to click on the legend to toggle visibility of categorical data.  When hiding a category in a stacked bar chart, the bar segments disappear as expected, however it would be nice to have the remaining bar segments change their position, slide over, so there are no gaps left in the plot.  

The attached file is a sample that will plot a horizontal stacked bar chart.  If you turn off the year 2016, you can see they disappear, but now I want the 2017 segments to slide to the left until they are against the 2015 segments.  Here is a link to better illustrate the request if it is not clear:
https://www.highcharts.com/demo/column-stacked

I posted this question on the forum and was told it was not possible at this time and suggested to create a new feature request. (https://discourse.bokeh.org/t/re-position-bar-segments-for-stacked-bar-plot/4887/2)

[Sample Code](https://github.com/bokeh/bokeh/files/4325787/bokeh_feature_request.txt)

user: hahnd
issue_9770: state: open
title: [BUG] on_change events of DataTable CDS fail when source is set after init
body: **Software version info**
bokeh 2.0.0
chrome 80.0.3987.132 (Official Build) (64-bit)
ubuntu 19.10

**Description of expected behavior and the observed behavior**
Context:
- An on_change('data', callback) is registered on the ColumnDataSource which is attached to a DataTable
- An on_change('indices', callback) is registered on the "selected" field of a ColumnDataSource which is attached to a DataTable

Expect:
- The callback will be called while the content in the DataTable is edited
- The callback will be called while a row in the DataTable is selected

Observed:
- Both callbacks are not called

**Complete, minimal, self-contained example code that reproduces the issue**
```
from bokeh.io import curdoc
from bokeh.models import TableColumn, ColumnDataSource, DataTable

columns = [TableColumn(field='a', title='a'),
             TableColumn(field='b', title='b'),
             TableColumn(field='c', title='c')]

cds = ColumnDataSource(dict(a=[1,2,3],b=[2,3,4], c=[3,4,5]))

table = DataTable(editable=True, width=300, height=150)
table.source = cds
table.columns = columns
def on_data(attr, old, new):
    print('on_data')

def on_select(attr, old, new):
    print('on_select')


table.source.on_change('data', on_data)
table.source.selected.on_change('indices', on_select)

curdoc().add_root(table)
```

user: maeglin89273
issue_9769: state: open
title: [FEATURE] Warnings should be reported for custom hover tool
body: The code below attempts to create a custom hover tool using Holoviews. 

However, this code is not successful and the custom function has no effect.

````
import holoviews as hv
import numpy as np
import panel as pn
import bokeh
from bokeh.resources import INLINE
from bokeh.models import HoverTool
from bokeh.models import CustomJSHover

pn.extension(safe_embed=True)
for library in [hv,pn,bokeh]:
    print ("%s version %s"% ( library, library.__version__))


hv.extension('bokeh') 

frequencies = [0.5, 0.75, 1.0, 1.25]

MyCustom = CustomJSHover(code=''' 
        return "test"
''')

MyHover1 = HoverTool(
    tooltips=[
        ( 'xvals', '@xvals'),
        ( 'yvals', '@yvals'),
        # the line below should be:
        # ( 'phase', '@phase{custom}'),
        # ( 'freq', '@freq{custom}'),
        ( 'phase', '@phase'),
        ( 'freq', '@freq'),
   ],
    formatters={
        'xvals' : 'numeral',
        'yvals' : 'numeral',
        # the lines below should be: 
        # '@phase' : MyCustom,
        # '@freq' : MyCustom,
        'phase' : MyCustom,
        'freq' : MyCustom,
    },
    point_policy="follow_mouse"
)

def sine_curve(phase, freq):
    xvals = [0.1* i for i in range(100)]
    data = {
        'xvals' : xvals,
        'yvals' : [np.sin(phase+freq*x) for x in xvals],
        'phase' : [phase for x in xvals],
        'freq' : [freq for x in xvals],
    }
    plot =  hv.Points( data, kdims = ['xvals','yvals'], vdims = ['phase','freq'])
    return plot


phases      = [0, np.pi/2, np.pi, 3*np.pi/2]
curve_dict_2D = {(p,f):sine_curve(p,f) for p in phases for f in frequencies}
hmap = hv.HoloMap(curve_dict_2D, kdims=['phase', 'frequency']).opts(tools = [MyHover1])

panel_Object = pn.pane.HoloViews(hmap)
panel_Object.save('SineHover.html', resources=INLINE, title = 'Hover Example', embed=True)

hv.save(hmap,'test_holoviews.html')
````
To fix this and let MyCustom function to execute, there is a need to do two changes in the Hover tool that are commented out in the code. When running the current ode, there is no error or warning message explaining what is wrong, yet instead of printing "test" for Phase an Freq, the system shows a hover tip where the freq and phase numbers are written:
![image](https://user-images.githubusercontent.com/1986155/76284358-16194900-626b-11ea-884e-cde5cad92148.png)

The expected result after fixing the lines should be:
![image](https://user-images.githubusercontent.com/1986155/76284406-3812cb80-626b-11ea-993f-b6fafcabe5e4.png)

Adding warnings or errors will help a user figure out how to properly create custom hover tools - there are not many examples and the user is many times clueless when not getting the expected results without understanding why.

The issues still exists in Bokeh 1.4.0.
user: Jacob-Barhak
issue_9767: state: open
title: Multiple output_file in a single process can fail when models are shared
body: I have 2 anaconda environments in Windows 7: 

- **env1** has jupyter lab 0.32 and bokeh 0.12.16, 
- **env2** has jupyter lab 2.0.1 and bokeh 2.0.0. 

In a jupyter notebook, I have 2 cells of code:

**Cell 1**
```
source1 = ColumnDataSource(fertility_df[fertility_df['Continent']=='ASI'])
p1 = figure(x_axis_label='fertility (children per woman)', y_axis_label='female literacy (% population)')
p1.circle('fertility', 'female literacy', source=source1)

source2 = ColumnDataSource(fertility_df[fertility_df['Continent']=='EUR'])
p2 = figure(x_axis_label='fertility (children per woman)', y_axis_label='female literacy (% population)')
p2.circle('fertility', 'female literacy', source=source2)

source3 = ColumnDataSource(fertility_df[fertility_df['Continent']=='AF'])
p3 = figure(x_axis_label='fertility (children per woman)', y_axis_label='female literacy (% population)')
p3.circle('fertility', 'female literacy', source=source3)

source4 = ColumnDataSource(fertility_df[fertility_df['Continent']=='LAT'])
p4 = figure(x_axis_label='fertility (children per woman)', y_axis_label='female literacy (% population)')
p4.circle('fertility', 'female literacy', source=source4)

row1 = [p1, p2]
row2 = [p3, p4]
layout = gridplot([row1, row2])
output_file('bokeh.html')
show(layout)
```
**Cell 2**
```
tab1 = Panel(child=p1, title='Latin America')
tab2 = Panel(child=p2, title='Africa')
tab3 = Panel(child=p3, title='Asia')
tab4 = Panel(child=p4, title='Europe')

layout = Tabs(tabs=[tab1, tab2, tab3, tab4])
output_file('bokeh.html')
show(layout)
```
In the `env1`, I could run cell 1, cell 2, and cell 1 again without any problem.

However, in `env2`, after executing cell 1, then trying to run cell 2, I got an error :
```
RuntimeError: Models must be owned by only a single document, WheelZoomTool(id='1130', ...) is already in a doc
```
My questions are:

- Is it a bug in bokeh 2.0.0?
- What does it mean by **Models** in the error message?
user: jaseuts
issue_9748: state: open
title: [BUG] Document not set on result of pull_session - Django/bokeh integration
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
bokeh - 1.4.0
python - 3.8.1
django - 3.0.2
OS - Windows 10
Browser - Google Chrome 80

#### Description of expected behavior and the observed behavior
* Expected behavior - pulling a document from an existing session (by session-id and url) should return the corresponding document.
* Observed behavior - While the session **is found**, the document is not set on it (during [`session.pull`](https://github.com/RyanRio/bokeh/blob/86e6613403d758955a0991c81cdfbe352933c68b/bokeh/client/session.py#L119))

#### Complete, minimal, self-contained example code that reproduces the issue
The example I use is a django application which has an embedded bokeh app. One simple method is added to the document like so:
```python
# in bokeh_handlers.py
def example(doc: Document):
    def cb(data):
        print(data)

    django_session_id = doc.session_context.request.arguments['django_session']
    session_id = doc.session_context.id
    BokehSession.objects.create(bokeh_session=session_id, django_session=django_session_id)
    # note that the above line would normally fail as this is an orm operation running in an async context, 
    # however in the actual code I run it in a separate thread, but I leave it like this for simplicity
    doc.my_cb = cb
    doc.my_cb("test") # prints test
```
[In `AutoloadJsConsumer(..)`](https://github.com/RyanRio/bokeh/blob/master/bokeh/server/django/consumers.py#L126) I add the following line:
```python
session.document.my_cb("in autoload") # prints in autoload
```
In [`WebsocketConsumer(..)`](https://github.com/RyanRio/bokeh/blob/master/bokeh/server/django/consumers.py#L246) I add the following line:
```python
session.document.my_cb("in websocket") # prints in websocket
```

The following is quick and dirty setup of bokeh/django integration
```python
# in urls.py
bokeh_apps = [
    autoload("example", bokeh_handlers.example)
]

urlpatterns = [
    path('example/', csrf_exempt(bokeh_handlers.Example.as_view())),
]

# in bokeh_handlers.py
from perf_dash.models import BokehSession

class Playground(TemplateView):
    template = "example.html"

    def get(self, request):
        if not request.session or not request.session.session_key:
            request.session.save()
        key = request.session.session_key
        script = server_document(request.build_absolute_uri(), arguments=dict(django_session=key))
        context = dict(script=self.script, test="ok")
        return render(request, self.template, context)

    def post(self, request):
        objs = BokehSession.objects.get(pk=request.session.session_key)
        if hasattr(objs, 'bokeh_session'):
            bokeh_id = objs.bokeh_session
            with pull_session(session_id=bokeh_id, url=request.build_absolute_uri()) as session:
                doc = session.document
                doc.my_cb("test") # err here
```
example.html:
```html
<button id="test" type="button">Example</button>
<script>
var $ = jQuery.noConflict();
$( document ).ready(function() {
    let ajax_dict = {
        type: 'POST',
        url: '/example',
        data: {
        },
        success: function(data) {
        }
    };
    $('#test').on('click', function(e){
        e.preventDefault();
        $.ajax(ajax_dict);
    });
});
</script>
```

#### Stack traceback and/or browser JavaScript console output
**Upon navigating to the page**
```
test
in autoload
HTTP GET /perf/playground/autoload.js?bokeh-autoload-element=1091&bokeh-app-path=/perf/playground&bokeh-absolute-url=http://ip:8002/example&django_session=4tjl0zbpnb7v4o2wfi2vn0vjd8t1oo13 200 [19.93, ip:55357]
INFO:django.channels.server:HTTP GET /example/autoload.js?bokeh-autoload-element=1091&bokeh-app-path=/example&bokeh-absolute-url=http://ip:8002/example&django_session=4tjl0zbpnb7v4o2wfi2vn0vjd8t1oo13 200 [19.93, ip:55357]
WebSocket HANDSHAKING /example/ws [ip:55370]
INFO:django.channels.server:WebSocket HANDSHAKING /example/ws [ip:55370]
WebSocket CONNECT /example/ws [ip:55370]
INFO:django.channels.server:WebSocket CONNECT /example/ws [ip:55370]
in websocket
```
**Upon clicking the `test` button which does `pull_session`**
```
WebSocket HANDSHAKING /example/ws [ip:58659]
INFO:django.channels.server:WebSocket HANDSHAKING /example/ws [ip:58659]
WebSocket CONNECT /example/ws [ip:58659]
INFO:django.channels.server:WebSocket CONNECT /example/ws [ip:58659]
in websocket
(doc.my_cb("test") is called right here)
...
AttributeError: 'Document' object has no attribute 'my_cb'
...
```
The ... is simply the full traceback and logging that the ws disconnected due to the error.

Note additionally that the session_id used everywhere (in `WSConsumer`, `AutoloadJsConsumer`, and `pull_session` are identical (I checked the value of them with a debugger).
#### Screenshots or screencasts of the bug in action

user: RyanRio
issue_9742: state: open
title: Notebook testing
body: We have been without proper CI testing for notebook and JupyterLab for too long. We need to make CI testing for these a top priority after 2.0. 
user: bryevdv
issue_9721: state: open
title: Add development docker image to docker-tools
body: This ticket is a follow up from #9716:

Add development docker image in dockertools following [the developers guide for getting set up](https://docs.bokeh.org/en/latest/docs/dev_guide/setup.html#devguide-setup)
- [ ] create Dockerfile from `dkapitan/continuumio:miniconda-debian-slim` that includes
  - [ ] installation of all dependencies via `environment.yml`
  - [ ] running `python setup.py develop`]
  - [ ] installing `bokeh sampledata`
- [ ] has Makefile for ease-of-use, e.g. launching container in interactive mode + volume mount of working directory on local machine following [Travis Reeder's setup](https://medium.com/better-programming/why-and-how-to-use-docker-for-development-a156c1de3b24)

Not sure what to do with Git Hooks. I think they don't need to go in the container.


user: dkapitan
issue_9719: state: open
title: Document/warn about log scale glyph limitations better
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
bokeh 1.4 python 3.7 google collaboratory

#### Description of expected behavior and the observed behavior

When rendered on a log scale with a width and height, ellipses should not be visually centered around their xy center. But bokeh renders them as such. Here's an example where an ellipse should touch at least some verticies of a polygon, but does not: 

![bokeh_log_ellipse](https://user-images.githubusercontent.com/479213/75138093-3c9d8880-569e-11ea-9312-a92138e25930.png)

#### Bugdemo source code
The source is [here](https://colab.research.google.com/drive/10FSd_8EFBpt1hfINWuucajJgYP0K_GES)

user: pde
issue_9694: state: open
title: Run JS unit tests in more real browsers
body: I *think* you could narrow this to just test the different JS engines. That is: v8 (chrome, chromium, edge, brave), gecko (firefox), webkit (safari). Off the top of my head I don't know what different mobile platforms are.

_Originally posted by @birdsarah in https://github.com/bokeh/bokeh/issues/5058#issuecomment-588530584_
user: bryevdv
issue_9693: state: open
title: [DOCUMENTATION] Glyph Line, Text, and Fill defaults in theme yaml are undocumented
body: The [reference manual page for themes](https://docs.bokeh.org/en/latest/docs/reference/themes.html) does not document the ability to set default line, text, and fill attributes for glyphs in the theme.yaml file.  For example this is valid:
```yaml
attrs:
  Axis: {}
  Grid: {}
  Plot: {}
  Title:
    text_color: '#f4a4c0'
  Toolbar: {}
fill_defaults:
  fill_color: '#e32400'
line_defaults:
  line_color: '#2b1276'
text_defaults:
  text_color: '#7a4900'
  text_font: Calibri
```

and has the effect, for example, of setting the default font in all Text Glyphs to "Calibri" unless specifically overridden by, for example, providing defaults for the Text Glyph.  The relevant code is in ```theme.py``` where the constructor for the ```Theme``` class includes:
```python
    self._line_defaults = self._json.get('line_defaults', _empty_dict)
    self._fill_defaults = self._json.get('fill_defaults', _empty_dict)
    self._text_defaults = self._json.get('text_defaults', _empty_dict)
```
and there is the method:
```python
def _add_glyph_defaults(self, cls, props):
        from ..models.glyphs import Glyph
        if issubclass(cls, Glyph):
            if hasattr(cls, "line_alpha"):
                props.update(self._line_defaults)
            if hasattr(cls, "fill_alpha"):
                props.update(self._fill_defaults)
            if hasattr(cls, "text_alpha"):
                props.update(self._text_defaults)
```

The documentation also includes the somewhat cryptic annotation:
```
    The plotting API's defaults override some theme properties. Namely:
    `fill_alpha`, `fill_color`, `line_alpha`, `line_color`, `text_alpha` and
    `text_color`. Those properties should therefore be set explicitly when
    using the plotting API.
```
which merits more explanation.
user: jeremy9959
issue_9688: state: open
title: [FEATURE] Filled bezier curves / bezier patch
body: I would like to be able to draw SVG objects (using Abode Illustrator or similar), and import these as manipulatable objects in bokeh. Here is a proof a concept that already works (with caveats, see below):

```python3
from svg.path import parse_path #pip3 install svg.path
from svg.path.path import Line
from xml.dom import minidom

from bokeh.models import ColumnDataSource, Plot
from bokeh.models.glyphs import Bezier
from bokeh.io import show

import pandas as pd

# read the SVG file
doc = minidom.parse('./Apple_logo_black.svg')
path_strings = [path.getAttribute('d') for path
                in doc.getElementsByTagName('path')]
doc.unlink()

#parse path and load into ColumnDataSource
l=[]
path=parse_path(path_strings[0])#example is only of len == 1
for p in path:
    try: #y coords need to be negative for some reason
        x0 = p.start.real
        y0 = -1 * p.start.imag
        x1 = p.end.real
        y1 = -1 * p.end.imag
        cx0 = p.control1.real
        cy0 = -1 * p.control1.imag
        cx1 = p.control2.real
        cy1 = -1 * p.control2.imag
        l.append((x0,y0,x1,y1,cx0,cy0,cx1,cy1))
    except AttributeError: #this excepts the "move to" object
        pass
l=pd.DataFrame(l,columns=["x0","y0","x1","y1","cx0","cy0","cx1","cy1"])
l=ColumnDataSource(l)

#plot
plot = Plot(
    title=None, plot_width=400, plot_height=400,
    min_border=0, toolbar_location=None)

glyph = Bezier(x0="x0", y0="y0", x1="x1", y1="y1", cx0="cx0", cy0="cy0", cx1="cx1", cy1="cy1", line_color="#000000", line_width=2)
plot.add_glyph(l,glyph)
show(plot)
```
file: https://commons.wikimedia.org/wiki/File:Apple_logo_black.svg

This draws the outlines of the SVG object, but it doesn't fill it. I believe that in an SVG file this is typically specified by a "start" and "end" point for each contiguous path which then a `fill` parameter can be passed. The implementation that I posted above is just a series of cubic bezier curves that happen to start/stop where each curve ends, thus not allowing a `fill` parameter to be passed. (side note: when learning about bezier curves I think the docs should specify that bokeh uses _cubic_ bezier curves) 

An ideal solution would be a [bezier spline](https://en.wikipedia.org/wiki/Composite_B%C3%A9zier_curve) patch, which allows a ColumnDataSource to be passed to it (like above example) along with a `fill` parameter. An SVG parser using existing python libraries seems relatively straightforward. The first and last row could be the starting and ending points that dictate the beziergon, or perhaps as separate parameters altogether.

Alternatively, is it already possible to generate a set of cartesian coordinates from my above example that I could feed into `glyphs.Patch` so I can fill it that way?

Thanks for the wonderful package and all the hard work!

user: mmcguffi
issue_9685: state: open
title: Improve sorting and grouping behavior on grouped Legends
body: **Is your feature request related to a problem? Please describe.**
Grouped legends are difficult to sort, and keep sorted if the underlying ColumnDataSource changes, because the LegendItems are created and sorted on the JS side. 

**Describe the solution you'd like**
A concise way to enforce a sort order (e.g. alphabetical) on grouped Legenditems.

**Describe alternatives you've considered**
The explicit creation and assignment of LegendItems as described [here](https://discourse.bokeh.org/t/cant-order-legend-entries-in-hbar-plot/3816/5) works for static datasets, although it is inconvenient and messy.

However, in a callback scenario where the underlying CDS changes (like examples [here](https://discourse.bokeh.org/t/why-is-this-legend-going-into-party-mode-rotating-across-colors-when-using-a-slider-with-a-callback/4544/15)), the labels do not "keep up" with the CDS and colors may get reassigned to the wrong grouping in the legend. (This may be a separate issue or a bug; I'm not sure.)

**Additional context**
The Discourse threads linked in the previous section include examples and screenshots, but I'm happy to provide any additional examples or information to further elucidate the issue.

user: ghost
issue_9673: state: open
title: [TEST] More tests on the spinner should be added
body: I open this issue to keep track of the task:

> I think more tests could be added, unit tests for some of the string/number munging, and integration tests that actually exercise the spinner. Can you make an issue for this?

_Originally posted by @bryevdv in https://github.com/bokeh/bokeh/pull/9355#issuecomment-549165803_
user: xavArtley
issue_9669: state: open
title: [FEATURE] Make it possible to write code without excessive usage of document callbacks
body: **Is your feature request related to a problem? Please describe.**
Consider this simple code:
```python
import asyncio

from bokeh.io import curdoc
from bokeh.layouts import column
from bokeh.models import Div, Button

d = Div(text="")
b = Button(label="Click me")


async def cb():
    d.text = "started"
    await asyncio.sleep(5)
    d.text = "finished"


def on_click(event):
    curdoc().add_next_tick_callback(cb)


b.on_click(on_click)

curdoc().add_root(column(d, b))
```
It looks great - there's nothing to simplify (except for maybe that unused `event` argument, but that's another story).
The issue is that when you press the button, there's no "started" text displayed. The document stays locked for the whole duration of `cb`, no updates get out during the time of its execution.

**Describe the solution you'd like**
The solution is to not lock the document for the duration of the document callbacks. Hard to say how exactly it could be implemented though, but it would be great to make the above code work.

Another, a bit more cumbersome, solution is to manually trigger sending of patches. Something like
```python
async def cb():
    d.text = "started"
    curdoc().synchronize()
    await asyncio.sleep(5)
    d.text = "finished"
```

Yet another, an even more cumbersome one, is to not lock the callback and manually lock everything that needs locking:
```python
@without_document_lock
async def cb():
    with await curdoc().lock():
        d.text = "started"
    await asyncio.sleep(5)
    with await curdoc().lock():
        d.text = "finished"
```

**Describe alternatives you've considered**
The only solution right now is to find the points in code where the document updates should be sent, split the code and those points, and reorganize the pieces in such a way so that you constantly call `doc.add_next_tick_callback()` on them to make sure that the document synchronization happens between the callbacks, where the document is not locked.
user: p-himik
issue_9663: state: open
title: [BUG] data value tooltip incorrect on figure.image with log scale
body: bokeh: 1.4.0
python: 3.7.5rc1
Jupyter: 1.0.0
OS: Linux, Ubuntu 19.10

I modified the example in Bokeh gallery called image.py so that one of the axis is in log scale.  While the figure is correct, the tooltips is not.  When hovering on a region with x being a multiple of pi, I expect to get a value close to 0.  But it usually does not.

My image map has x value between 1 to 10, in log scale.  In this range there should be 3 multiple of pi, where the value should be around 0.  I found that there are actually 3 vertical lines where the value is around 0, but they are at the wrong place: somewhere near x=5.5, 8.1 and 9.5, instead of the expected 3.1, 6.3 and 9.4.

So it looks to me that somewhere in the code the transformation bringing the screen coordinate to the axes coordinate is done twice incorrectly.

## Code that reproduces the issue

```
import numpy as np
from bokeh.plotting import show

N = 500
x = np.logspace(0, 1, N)  # This is where the log scale data comes from
y = np.linspace(0, 10, N)
xx, yy = np.meshgrid(x, y)
d = np.sin(xx)*np.sin(yy)

# Here x-axis is configured to be log scale
p = figure(tooltips=[("x", "$x"), ("y", "$y"), ("value", "@image")], x_axis_type="log")
p.x_range.range_padding = p.y_range.range_padding = 0

p.image(image=[d], x=1, y=0, dw=9, dh=10, palette="Spectral11")
show(p)
```

#### Screenshot

![tooltip-bug](https://user-images.githubusercontent.com/1463929/74511243-8a005400-4f40-11ea-8837-99a8a62014e1.png)

user: isaacto
issue_9661: state: open
title: Allow configuring location of lifecycle and request handler hooks per app
body: Directory style apps have long supported a `server_lifecycle.py` file to define lifecycle hooks. In #9536 we added support for loading these hooks (and an additional `process_request` hook) from a `app_hooks.py` file. Ideally this would be configurable per app.
user: philippjfr
issue_9657: state: open
title: [BUG] Impossible to update `DataCube`'s data source
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
Bokeh 2.0.0dev6

#### Description of expected behavior and the observed behavior
Triggering `source.change.emit()` on the data source of a data cube results in
```
TypeError: Cannot read property '__bkdt_internal_index__' of undefined
    at bokeh-tables-2.0.0dev6.js:509
    at Array.sort (<anonymous>)
    at DataCubeProvider.sort (bokeh-tables-2.0.0dev6.js:507)
    at DataCubeView.updateGrid (bokeh-tables-2.0.0dev6.js:562)
    at DataCubeView.<anonymous> (bokeh-tables-2.0.0dev6.js:532)
    at DataCubeView.new_slot (bokeh-2.0.0dev6.js:6134)
    at Signal0.emit (bokeh-2.0.0dev6.js:2428)
    at Signal0.emit (bokeh-2.0.0dev6.js:2437)
    at ButtonClick.eval (eval at get func (bokeh-2.0.0dev6.js:17434), <anonymous>:5:43)
    at CustomJS.execute (bokeh-2.0.0dev6.js:17437)
```
The issue is that the base `DataTable` class attempts to sort the index when the data source is changed. And `DataCube` doesn't have any index column as it used the `slickgrid`'s `Grid` class for the rows.

Specifying `sortable=False` on the `DataCube` instance helps with the exception, and pressing the button leads to the leaf values being updated. However, the group nodes are not updated because none of the relevant data source signals are connected to anything `DataCube`-related.
A workaround is to use this button callback instead, that emits the `cube.change` signal:
```python
CustomJS(args=dict(source=source, cube=cube),
         code="""\
             source.data.px = source.data.px.map((px) => px * 2);
             source.change.emit();
             cube.change.emit();
         """)
```

#### Complete, minimal, self-contained example code that reproduces the issue

```python
from bokeh.io import show
from bokeh.layouts import row
from bokeh.models import ColumnDataSource, DataCube, GroupingInfo, StringFormatter, SumAggregator, TableColumn, Button, \
    CustomJS

source = ColumnDataSource(data=dict(
    d0=['A', 'E', 'E', 'E', 'J', 'L', 'M'],
    d1=['B', 'D', 'D', 'H', 'K', 'L', 'N'],
    d2=['C', 'F', 'G', 'H', 'K', 'L', 'O'],
    px=[10, 20, 30, 40, 50, 60, 70],
))

target = ColumnDataSource(data=dict(row_indices=[], labels=[]))

columns = [
    TableColumn(field='d2', title='Name', width=80, sortable=True, formatter=StringFormatter(font_style='bold')),
    TableColumn(field='px', title='Price', width=40, sortable=True)
]

grouping = [
    GroupingInfo(getter='d0', aggregators=[SumAggregator(field_='px')]),
    GroupingInfo(getter='d1', aggregators=[SumAggregator(field_='px')])
]

cube = DataCube(source=source, columns=columns, grouping=grouping, target=target)

b = Button(label='Double all')
b.js_on_click(CustomJS(args=dict(source=source),
                       code="""\
                           source.data.px = source.data.px.map((px) => px * 2);
                           source.change.emit();
                       """))

show(row(cube, b))
```

user: p-himik
issue_9653: state: open
title: [BUG] In some situations, Slider may lose its `on_change` callbacks
body: ### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
Bokeh 2.0.0dev6 (ad6dbf076fe94ffd5613a83338ba9517ca510320)

#### Description of expected behavior and the observed behavior
After receiving the initial pull doc response, a client generates a diff (because some models may change something during instantiation) and sends the resulting patch back.
If an instance of `Slider` managed to make its way into the `references` field of the patch, its `on_change` callbacks will be lost.

When you run the code below with `bokeh serve`, you won't see any `on change` in the stdout.

#### Complete, minimal, self-contained example code that reproduces the issue
Note that this might be a bit of a contrived example since I'm trying to create a legend that can be used in the main layout. But it can be used to demonstrate the behavior.
In this particular instance, the doc patch includes the slider in its `references` because the legend plot changes its `renderers` in `initialize`.

Since the code that computes the initial patch is quite contrived, I didn't try to investigate it any further.
But I have a feeling that it's a serious issue that could be reproduced with a more generic example. And it may be not specific to `Slider`.

There's this comment that seems to be relevant, but I'm not sure. Especially given that I don't see any model-specific initialization in `Slider` right now.
https://github.com/bokeh/bokeh/pull/8646/commits/9e3b04a3d394c6f63db295352f24547ebba18fa9#diff-b1481aa58345cdcc6dd59c391f19f7b4R85-R89

```python
from bokeh.core.property.instance import Instance

from bokeh.io import curdoc
from bokeh.layouts import column
from bokeh.models import Slider, ColumnDataSource, CDSView, CustomJSFilter, CustomJS, Plot, Legend, Label
from bokeh.plotting import figure


class LegendPlot(Plot):
    legend = Instance(Legend)

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        # Adding a dummy renderer to suppress the `MISSING_RENDERERS` warning.
        self.renderers.append(Label())

    # language=TypeScript
    __implementation__ = """
import {Plot} from "models/plots";
import {FrameBox, PlotView, RangeInfo} from "models/plots/plot_canvas";
import {Legend} from "models/annotations";
import * as p from "core/properties"
import {Class} from "core/class";
import {Context2d} from "core/util/canvas";
import {RenderLevel} from "core/enums";
import {Omit} from "core/types";
import {LegendView} from "models/annotations/legend";

declare var Bokeh: any;
Bokeh.set_log_level('trace');

export class LegendPlotView extends PlotView {
    model: LegendPlot;

    protected _paint_levels(ctx: Context2d, levels: RenderLevel[], clip_region: FrameBox, global_clip: boolean): void {
        for (const level of levels) {
            if (level == 'annotation') {
                super._paint_levels(ctx, ['annotation'], clip_region, global_clip);
                return;
            }
        }
    }

    async build_renderer_views(): Promise<void> {
        await super.build_renderer_views();
        for (const view_id in this.renderer_views) {
            const view = this.renderer_views[view_id];
            if (view instanceof LegendView) {
                const size = view.get_size();
                this.model.frame_width = size.width;
                this.model.frame_height = size.height;
                break;
            }
        }
    }

    reset(): void {}
    update_range(_range_info: RangeInfo | null, _is_panning: boolean = false,
                 _is_scrolling: boolean = false, _maintain_focus: boolean = true): void {}
    update_dataranges(): void {}
}

export namespace LegendPlot {
    export type Attrs = Omit<p.AttrsOf<Props>, "width" | "height">;

    export type Props = Plot.Props & {
        legend: p.Property<Legend>
    };
}

export interface LegendPlot extends LegendPlot.Attrs {
}

export class LegendPlot extends Plot {
    properties: LegendPlot.Props;
    default_view: Class<LegendPlotView>;

    initialize(): void {
        super.initialize();
        this.center.push(this.legend);
        for (const item of this.legend.items) {
            this.renderers.push(...item.renderers);
        }
    }

    static initClass(): void {
        this.prototype.default_view = LegendPlotView;

        this.define<LegendPlot.Props>({
            legend: [p.Any]
        });

        this.override({
            toolbar_location: null,
            border_fill_color: null
        });
    }
}

LegendPlot.initClass();
"""


f = figure()
s = Slider(start=0, end=9, step=1, value=5)

ds = ColumnDataSource(data=dict(x=list(range(10)), y=list(range(10))))
c = f.circle(x='x', y='x',
             source=ds,
             view=CDSView(source=ds,
                          filters=[CustomJSFilter(args=dict(s=s),
                                                  # language=JavaScript
                                                  code="""
                                                  return source.data.x.reduce((acc, val, idx) => {
                                                      if (val >= s.value) {
                                                          acc.push(idx);
                                                      }
                                                      return acc;
                                                  }, [])
                                              """)]))

l = LegendPlot(legend=Legend(items=[('Circle', [c])],
                             orientation='horizontal', margin=0, glyph_height=30, glyph_width=30))


def on_change(attr, old, new):
    print('on change', attr, old, new)


s.on_change('value', on_change)
s.js_on_change('value', CustomJS(args=dict(ds=ds),
                                 # language=JavaScript
                                 code="ds.change.emit();"))

curdoc().add_root(column(s, l, f))

```

user: p-himik
issue_9644: state: open
title: [DOCUMENTATION] Figure properties should be listed
body: The bokeh.plotting.Figure reference documentation mentions the following (bold mine):

> In addition to all the **Figure** property attributes, the following options are also accepted:

This seems not really helpful to me to figure out which properties are actually accepted by Figure. As `Figure` is basically a Plot with some helper methods, replacing `Figure` by `Plot` seems to make more sense to me:

> In addition to all the Plot property attributes, the following options are also accepted:

Personally I would also link Plot to make navigation as easy as possible for users.
user: syntonym
issue_9631: state: open
title: scale_both sizing mode appears to behave just like scale_width
body: This issue is similar to #7978 and could be a regression issue, or I might be misunderstanding this expected behavior. I also tried [this suggestion](https://github.com/bokeh/bokeh/issues/7978#issuecomment-396379757) by @mattpap to work around the issue, but to no avail because the size of the plot is controlled by scripted logic. `sizing_mode='stretch_both'` is working as expected, however `sizing_mode='scale_both'` is not. 

I tested this in Firefox 72.0.2 and Chromium 67.0.3396.99, resulting in the same behavior.

This screen recording shows comparison between `scale_both`, `stretch_both`, `scale_width`, and finally `scale_both` compared with `scale_both` again, where the latter two appear to behave identically (as reported in #7978) when (for example) resizing the browser window.

![2020-02-03_13-00-37](https://user-images.githubusercontent.com/16038281/73692100-84755500-4688-11ea-9732-ef4ea78917ab.gif)

##### environment
```
(.venv) >systeminfo

...
OS Name:                   Microsoft Windows 10 Pro
OS Version:                10.0.17763 N/A Build 17763
...
System Type:               x64-based PC
...

(.venv) >python --version
Python 3.6.5 :: Anaconda, Inc.

(.venv) >conda --version
conda 4.6.14

(.venv) >pip freeze
bokeh==1.4.0
Jinja2==2.11.1
MarkupSafe==1.1.1
numpy==1.18.1
packaging==20.1
Pillow==7.0.0
pyparsing==2.4.6
python-dateutil==2.8.1
PyYAML==5.3
six==1.14.0
tornado==6.0.3

```

##### sb.py
```python3
from bokeh.models import Plot, Range1d, ColumnDataSource,\
    PanTool, WheelZoomTool
from bokeh.models.glyphs import ImageURL
from bokeh.io import curdoc, save
from bokeh.io.state import curstate
from bokeh.util.browser import NEW_PARAM, get_browser_controller
from jinja2 import Template


def show_with_template(obj, template, browser=None, new='tab'):
    state = curstate()
    controller = get_browser_controller(browser)
    file_name = save(obj, state=state, template=template)
    controller.open('file://' + file_name, new=NEW_PARAM[new])


def main():
    template = Template("""\
    {% extends base %}
    {% block postamble %}
    <style>
    html, body, .bk-root {
        width: 100%;
        height: 100%;
        margin: 0;
    }
    </style>
    {% endblock %}
    """)

    plot = Plot(title=None,
                x_range=Range1d(start=0, end=1),
                y_range=Range1d(start=0, end=1),
                sizing_mode='scale_both')

    wheel_zoom = WheelZoomTool()
    plot.add_tools(PanTool(), wheel_zoom)
    plot.toolbar.active_scroll = wheel_zoom

    source = ColumnDataSource(dict(
        url=['https://static.bokeh.org/logos/logo.png'],
        x=[0],
        y=[0],
        w=[1],
        h=[1],
    ))

    image = ImageURL(url='url', w='w', h='h', x='x', y='y',
                     anchor='bottom_left')
    plot.add_glyph(source, image)

    doc = curdoc()
    doc.template = template
    doc.add_root(plot)
    show_with_template(plot, template)


if __name__ == '__main__':
    main()
```
user: tarocco
issue_9616: state: open
title: [FEATURE] Add export to PNG/SVG functionality to save tool
body: First of all: thanks creating this amazing framework.

A useful addition I would find useful would be to add the functionality to export to different formats in the save tool. Similar to what is offered in matplotlibs wx backend. I.e. 

![Screenshot 2020-01-27 at 14 46 04](https://user-images.githubusercontent.com/19503424/73184173-ef1c1300-4113-11ea-8d84-2b2516f50a8b.png)

Probably starting out with way fewer formats (PDF, SVG and PNG are probably the most important ones). This would be incredibly useful for scientific users to allow the export of publication quality images for use in illustration programs. 

From reading a couple of related posts, I am not sure whether this is achievable without enormous effort. Yet, it would be great!  

EDIT: I just found this discussion here in the discourse forum. It looks like other users would appreciate this feature, too.

https://discourse.bokeh.org/t/browser-plotting-backend-cannot-be-changed-on-the-fly/3319



user: drdqde
issue_9613: state: open
title: Consistency for in-place CDS updates
body: I have a CDS which fires a python callback, but not the JS callback. I am under the impression that both should fire at the same event. In the example below when I modify a cell in the DataTable, the python callback is called, but the JS callback is not.

From Bryan at discourse.bokeh

> Seems like something to discuss and figure out, please file a GitHub issue with all this information.
> 
> Normally, events are only fired when the entire value of a property changes, not when a subitem (e.g. when all of .data is re-assigned, not when a single buried inside .data changes changes). So offhand I’m not sure the python property should be triggering a callback here, when a single element in the table column is changed. The best solution might be to make things consistent with the rest of bokeh and making the python property not fire (consistency is good), but adding some other event that can be watched for “subitem” changes since in this specific case of the table (it’s a common need).

Python 3.7
bokeh 1.4.0

Simple example below:

```
from bokeh.models import DataTable, ColumnDataSource, CustomJS, TableColumn
from bokeh.plotting import curdoc

source = ColumnDataSource(data = { 'x':[0,1,2,3], 'y':['a','b','c','d']})
columns = [TableColumn(field='x'), TableColumn(field='y')]
data_table = DataTable(source=source,columns= columns, editable=True)

callback_hello_js = CustomJS(code='''
    // just logs to the console
    console.log('hello from js');
    ''')

def callback_hello_py(attr, old, new):
    print('hello from python')

source.on_change('data',callback_hello_py)
source.js_on_change('data',callback_hello_js)

curdoc().add_root(data_table)
```


user: nick-0
issue_9605: state: open
title: AutocompleteInput does not reflect changes in the value attribute
body: bokeh --version
1.4.0

python --version
Python 3.7.6

Expected: AutocompleteInput reflects any text changes made in the text input box, no matter how they got there.
Actual: When changing the value in the textbox, the values reflected in the textbox are the last change made from the completions list.

It might easily be different than what the value property of the widget actually holds. The changes do not trigger the on_change method.

I used the following example, taken from another issue.

```python
from bokeh.models.widgets.inputs import AutocompleteInput
from bokeh.plotting import curdoc

input_box = AutocompleteInput()
input_box.title = "Foo Bar"
input_box.value = "400"
input_box.completions = ["100001", "12344556", "3194567289", "209374209374"]


def update():
    print(input_box.value)


input_box.on_change('value', lambda attr, old, new: update())

curdoc().add_root(input_box)
```

The user inputs 10 and selects 100001.
The user tries options starting with 95. There aren't any.
The number 95 remains in the textbox, while the widget's value remains 100001.
The worst part is that if the user decides to re-select 100001, the option of 100001 appears, but upon selecting it, the textbox still shows 95.

The same holds if, for some reason, we would like to allow the user to input other values not in the Autocomplete list of options. The change does not trigger the attribute change, so we can't use the entered value.

user: nirAlt19
issue_9602: state: open
title: [FEATURE] equal_header_width option on tabs
body: Hi,
What about to implement "equal_header_width option (would need to find a better name) on tabs that ensures that the tab headers are all the same width."?
Or somehow guarantee the width of each individual tab - but then fonts and character sizes in pixels start to mess the picture.

You can see the suggestion here: https://github.com/bokeh/bokeh/issues/8726#issuecomment-577681289
And the illustration of the problem here: holoviz/panel#983.

Thanks in Advance!
user: thoth291
issue_9597: state: open
title: Flaky integration tests
body: Although much better now, there are still a few flaky Selenium tests. Seems to be mostly some of the edit tool tests. It's possible the issue is down to explicit waits as it was in the case of other flaky tests. 

user: bryevdv
issue_9576: state: open
title: [FEATURE] Allow enforcement of plot options on javascript side
body: This is specific to use cases where you are using bokehJS to render plot JSONs you do not directly control. An example use case is a dashboarding app where you allow users to create plots in python but are not giving them control over the web app side (my use case).

I'd like to be able to do things like disable custom Javascript for security, and enforce sizing modes to 'stretch_both'. I understand this would probably break some plots but in this use case I'd rather the plot looked busted and have the author fix it than wrecking the entire dashboard.
user: shughes-uk
issue_9570: state: open
title: Hover tool activated by click rather than hover for hand held devices [FEATURE] 
body: When trying to create a plot with a hover tool, only hovering over the plot triggers it and clicking usually does something else, like imitate dragging. 

On some hand held devices like cell phones it is not possible to hover over an image being displayed in an html page, touching the screen becomes a click.

It will be nice if there will be an option to detect the device or set the device on a bokeh tool that will interpret a screen click as a hover and display the hover information of the clicked entity.

This will make bokeh plots much more useful on hand held devices since a lot of information provided in the hover is currently not accessible on these devices. Considering that [mobile browsing  surpassed regular web browsing in 2016](https://www.theguardian.com/technology/2016/nov/02/mobile-web-browsing-desktop-smartphones-tablets), this will really help improve experience for a large audience.


user: Jacob-Barhak
issue_9568: state: open
title: [BUG] vbar_stack with DataFrame as source ignores entire row if first data column is NaN
body: # Software versions
OS: Linux 5.4.6
Browser: Chrome 78.0.3904.108 (Official Build) (64-bit)
Python: 3.8.1
JupyterLab: 1.2.4
Pandas: 0.25.3
Bokeh: 1.4.0

# Issue
When plotting a `vbar_stack`, the entire row in the data source is ignored if the first input column contains a NaN in a `pandas.DataFrame`.

If a standard Python `dict` is used as a data source, the output is plotted as expected.

```
from bokeh.plotting import figure, show
from bokeh.models import ColumnDataSource

data = dict(index=[1, 2, 3, 4],
            a=[4, 5, None, 7],
            b=[9, None, 7, 6])

source = ColumnDataSource(data)

f = figure()
f.vbar_stack(x="index", stackers=["a", "b"], source=source,
             width=0.5, color=["red", "blue"])
show(f)

source = ColumnDataSource(pd.DataFrame(index=[1, 2, 3, 4],
                                       data=[dict(a=4, b=9),
                                             dict(a=5, b=None),
                                             dict(a=None, b=7),
                                             dict(a=7, b=6),
                                            ]))

f = figure()
f.vbar_stack(x="index", stackers=["a", "b"], source=source,
             width=0.5, color=["red", "blue"])
show(f)
```

## Expected behavior with dict as data source

![Expected: dict as data source](https://user-images.githubusercontent.com/5302291/71999553-eb4a4e80-3241-11ea-90b0-629ac1c0f3bb.png)

## Unexpected behavior with DataFrame as data source

![Unexpected: DataFrame as data source](https://user-images.githubusercontent.com/5302291/71999567-f2715c80-3241-11ea-8f85-cd42e623ecdc.png)

# Mitigation

I currently let Pandas fill the `NaN`s with zeros.

Thanks for Bokeh, guys!
user: pohlt
issue_9550: state: open
title: multi_line broken when passing numpy arrays
body: bokeh version 1.4.0
python 3.7.0
jupyter notebook 6.0.2
macOS 10.15.2

#### Description of expected behavior and the observed behavior

This should work:

```
import bokeh.plotting
fig = bokeh.plotting.figure()
fig.multi_line(numpy.array([[1, 3, 2], [3, 4, 6]]), numpy.array([[2, 1, 4], [4, 7, 8]]))
```

However, it doesn't (see error message below).

For the record, the following code, without using numpy arrays, works without errors:

```
import bokeh.plotting
fig = bokeh.plotting.figure()
fig.multi_line(([[1, 3, 2], [3, 4, 6]]), ([[2, 1, 4], [4, 7, 8]]))
bokeh.io.show(fig)
```

#### Complete, minimal, self-contained example code that reproduces the issue

see above.

#### Stack traceback and/or browser JavaScript console output

Error produced with example above:

```
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
<ipython-input-60-d6c8a8fa1428> in <module>
      1 import bokeh.plotting
      2 fig = bokeh.plotting.figure()
----> 3 fig.multi_line(numpy.array([[1, 3, 2], [3, 4, 6]]), numpy.array([[2, 1, 4], [4, 7, 8]]))

fakesource in multiline(self, xs, ys, **kwargs)

~/anaconda3/envs/nteract/lib/python3.7/site-packages/bokeh/plotting/helpers.py in func(self, **kwargs)
    890         glyph_ca = _pop_colors_and_alpha(glyphclass, kwargs)
    891         incompatible_literal_spec_values = []
--> 892         incompatible_literal_spec_values += _process_sequence_literals(glyphclass, kwargs, source, is_user_source)
    893         incompatible_literal_spec_values += _process_sequence_literals(glyphclass, glyph_ca, source, is_user_source)
    894         if incompatible_literal_spec_values:

~/anaconda3/envs/nteract/lib/python3.7/site-packages/bokeh/plotting/helpers.py in _process_sequence_literals(glyphclass, kwargs, source, is_user_source)
    373 
    374         if isinstance(val, np.ndarray) and val.ndim != 1:
--> 375             raise RuntimeError("Columns need to be 1D (%s is not)" % var)
    376 
    377         if is_user_source:

RuntimeError: Columns need to be 1D (xs is not)
```

#### Screenshots or screencasts of the bug in action

user: poke1024
issue_9538: state: open
title: The layout of Markdown with images does not work well
body: FIRST POSTED TO THE PANEL ISSUE LIST HERE https://github.com/holoviz/panel/issues/835

@philippjfr SUGGESTED POSTING IT HERE WITH THE COMMENT *I think the main issue here is the latency in loading the image, bokeh's layout engine tries to measure the size of the component before the image is fully loaded. We should probably file an issue with bokeh to ensure that measurements wait on external components to be fully loaded*.

#### System Info

- Panel: 0.7.0
- Bokeh: 1.4.0
- Python: 3.7.4
- OS: Windows 8.1
- Browser: Chrome

#### Description of expected behavior and the observed behavior

If I do a simple layout using columns and markdown with an image I would expect it to display nicely. 

It does not. Instead it displays on top of each other.

![image](https://user-images.githubusercontent.com/42288570/70017590-d106bc00-1583-11ea-9dfb-8d5b5f3f4399.png)

#### Complete, minimal, self-contained example code that reproduces the issue

```python
import panel as pn

ABOUT = """\
# About

[Panel](https://panel.pyviz.org/) is a powerfull framework for building awesome-analytics apps in [Python](https://www.python.org/).

The purpose of this app is to test that a **multi-page Dashboard Layout** similar to the [bootstrap dashboard template](https://getbootstrap.com/docs/4.3/examples/dashboard/) from [getboostrap.com](https://getbootstrap.com/) can be implemented in [Panel](https://panel.pyviz.org/).

[![GetBootstrap Dashboard Template](https://getbootstrap.com/docs/4.4/assets/img/examples/dashboard.png)](https://getbootstrap.com/docs/4.3/examples/dashboard/)
"""

INFO = """\
Navigate to the **Dashboard Page** via the **Sidebar** to see the result.
Or Navigate to the **Limitations Page** to learn of some of the limitations of Panel that
I've experienced."""

about = pn.pane.Markdown(ABOUT)
info = pn.pane.Markdown(INFO, background="#d1ecf1")
app = pn.Column(about, info)
app.servable()
```

### Additional Context

If I then resize or refresh the window. Then it displays correctly.


![issue_markdown_w_image_markdown](https://user-images.githubusercontent.com/42288570/70017452-6ce3f800-1583-11ea-98e7-f47204b77d33.gif)

I have a feeling the Bokeh javascript some how lays out the page before actual knowing that there is a image taking up some space. But that's the feeling.
user: MarcSkovMadsen
issue_9535: state: open
title: Bokeh maps tiles not filling in
body: Bokeh doesn't seem to update the basemap tiles as well as expected. The example below shows some functionality, even good performance in some map areas, but sometimes the zoom-in, zoom-out, or pan functions cause blank tiles to show up that never fill in.

I [raised this question in the Community Support](https://discourse.bokeh.org/t/how-to-request-bokeh-maps-tile-updates/4431) `discourse` and @Bryan suggested reviewing  regressions with `TileProvider` plots.

Something isn't requesting or confirming the network tile updates.

Here’s a simple code clip to convert (lat/lon) pairs into Northing and Easting values to display a region of a basemap:

    from bokeh.plotting import figure, show, output_file, curdoc
    from bokeh.models.widgets import Slider, Div
    from bokeh.layouts import column, layout
    from bokeh.tile_providers import get_provider, Vendors
    from pyproj import Proj, transform

    def LatLon_to_EN(lat, lon):
        try:
          easting, northing = transform( Proj('epsg:4326'), Proj('epsg:3857'), lat, lon) # from WGS-84 to Web Mercator Easting/Northing
          return easting, northing
        except:
          return None, None

    tile_provider = get_provider(Vendors.STAMEN_TERRAIN_RETINA)
    #tile_provider = get_provider(Vendors.STAMEN_TERRAIN)

    # SouthWest and NorthEast corners
    SW_corner=(29.191391, -81.049014) # center of campus foot bridge
    NE_corner=(29.239598, -80.949180) # out in the ocean

    # Convert to mercator Easting and Northing coordinates
    SW_corner_EN = LatLon_to_EN( SW_corner[0] , SW_corner[1] )
    NE_corner_EN = LatLon_to_EN( NE_corner[0] , NE_corner[1] )

    # x_axis_type, y_axis_type may be: "linear", "log", "datetime", "mercator"
    plot = figure(x_range=(SW_corner_EN[0], NE_corner_EN[0]), y_range=(SW_corner_EN[1], NE_corner_EN[1]), x_axis_type="mercator", y_axis_type="mercator")
    plot.add_tile(tile_provider)

    curdoc().add_root(plot)

This command runs without any error messages on Ubuntu 18.04.3LTS in the latest Chrome (Version 78.0.3904.108 (Official Build) (64-bit)):

    bokeh serve --show bokeh_alt_tile_providers_for_posting_online.py

Thank you,
Marc Compere







user: comperem
issue_9523: state: open
title: [FEATURE] Documentation for Prebuilt Extensions
body: **Is your feature request related to a problem? Please describe.**
While the documentation of the inline extension is long and detailed, pre-built extensions are only talked about shortly. 
Especially, the correct setup for a pre-built extension is not explained.

**Describe the solution you'd like**
Provide setup instructions and/or a complete pre-built example for a extension, especially the usage in code.

user: sebimarkgraf
issue_9508: state: open
title: [BUG] TextInput always moves cursor to end of the edit field after typing a character
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
Configuration 1:
Bokeh 1.4.0
Bokeh Server OS is Debian 10
Client OS is macOS 10.14.6
Browser is Safari 13.0.1

Configuration 2:
http://docs.bokeh.org/en/1.4.0/docs/user_guide/interaction/widgets.html#textinput
Client OS is macOS 10.14.6
Browser is macOS 10.14.6

#### Description of expected behavior and the observed behavior
Expected behavior: If I move the cursor to the middle of the TextInput field and start typing, my characters should be inserted in the middle of the word and the cursor position should stay immediately after the last character I typed.

Observed behavior: If I move the cursor to the middle of the TextInput field and type a single character, the character is inserted in the expected location but then the cursor position snaps to the end of the input field and subsequent characters typed are appended to the end of the input.

#### Complete, minimal, self-contained example code that reproduces the issue
The sample code on the widget example page linked above reproduces it in a standalone document.  The same problem occurs in my environment under a bokeh server but I don't think we need to include the complex code for that since the standalone document has the same issue.

user: tgree
issue_9504: state: open
title: [BUG] Repeated plotting in Jupyter Lab increases (browser) memory usage
body: I am using **Bokeh** to **plot** many time-series (>100) with many points (~20,000) within a **Jupyter Lab** Notebook.
When **executing the cell multiple times** in Jupyter the **memory** consumption of Chrome **increases per run by over 400mb**. After several cell executions Chrome tends to crash, usually when several GB of RAM usage are accumulated. Further, the plotting tends to get slower after each execution.

A *"Clear [All] Outputs"* or *"Restart Kernel and Clear All Outputs..."* in Jupyter also does not free any memory. In a classic **Jupyter Notebook** as well as with **Firefox** or **Edge** the issue also occurs.

Minimal version of my .ipynp:
```
import numpy as np
from bokeh.io import show, output_notebook
from bokeh.plotting import figure
import bokeh
output_notebook()
```
```
# Just create a list of numpy arrays with random-walks as dataset
ts_length = 20000
n_lines = 100
np.random.seed(0)
dataset = [np.cumsum(np.random.randn(ts_length)) + i*100 for i in range(n_lines)]
```
Repeatedly running this cell leads to a sever memory accumulation:
```
# Plot exactly the same linechart every time
plot = figure(x_axis_type="linear")
for data in dataset:
    plot.line(x=range(ts_length), y=data)
show(plot)
```

<br />
Installed Versions on my System (Windows 10):

- Python 3.6.6 : Anaconda custom (64-bit)
- bokeh: 1.4.0
- Chrome: 78.0.3904.108 
- jupyter:
  - core: 4.6.1 
  - lab: 1.1.4
  - ipywidgets: 7.5.1
  - labextensions:
     - @bokeh/jupyter_bokeh: v1.1.1
     - @jupyter-widgets/jupyterlab-manager: v1.0.*

As suggested in an answer to [my question on Stackoverflow](https://stackoverflow.com/questions/59180972/bokeh-repeated-plotting-in-jupyter-lab-increases-browser-memory-usage/), this might be a bug with bokeh. 

user: pchalupar
issue_9501: state: open
title: Improve hot reload speed and process to enable fast development-test cycle
body: THIS ISSUE WAS FIRST FILED TO PANEL HERE https://github.com/holoviz/panel/issues/849

BUT I WAS INFORMED ITS A FEATURE REQUEST FOR BOKEH

#### System Info

- Panel: 0.7.0
- Bokeh: 1.4.0
- Python: 3.7.4
- OS: Windows 8.1
- Browser: Chrome

#### My Pain

When developing apps in Panel if often wan't to see how the change looks. It's possible to do by using the `--dev` flag but

- The restart of the Panel/ Bokeh server is very slow compared to the alternative technologies of Streamlit and Angular that I'm used to.
- The browser window does not automatically reload. I have to wait for the bokeh server to reload and then I can click refresh. In Streamlit and Angular I do not have to do this. I don't have to do this when I work with Sphinx either.

![hot_reload_process_slow](https://user-images.githubusercontent.com/42288570/70203790-68a01200-171e-11ea-9d20-400bd9fed99f.gif)

(Note I have to run panel server as a *python module* in order for reload to work on windows).

#### Solutions

- Improve restart time for Panel/ Bokeh server
- Enable automatic reload of the browser.

I believe enabling automatic reload of the browser alone would be very, very valuable if its difficult to improve the restart time for the Panel/ Bokeh server.
user: MarcSkovMadsen
issue_9475: state: open
title: Area glyph does not recognise missing data points (nans)
body: ### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)

bokeh.version: 1.3.4
Python version: 3.7.3
OS: GNU/Linux x86_64
Browser: Google Chrome 77.0.3865.90
JupyterLab version: 1.1.3

#### Description of expected behavior and the observed behavior

When plotting an area chart that contains missing values, I would expect that there would be a corresponding gap in the area glyph. However, Bokeh seems to interpolate the data, as shown in the example below. This is different to the behaviour in other plotting libraries such as matplotlib.

This issue may be related to #7890 - it looks like a similar issue has already been fixed for the **line** and **step** glyph, but not yet **area**.

#### Complete, minimal, self-contained example code that reproduces the issue

```python
from bokeh.plotting import figure, output_notebook, show
import matplotlib.pyplot as plt
import numpy as np

# Example data
y = [0,1,2,3,4,5]
x1 = [1,2,np.NaN,np.NaN,1,1]
x2 = [5,7,np.NaN,np.NaN,5, 6]

# Bokeh plot
f = figure()
f.harea(y=y, x1=x1, x2=x2)
show(f)

# Matplotlib plot (for comparison)
f, ax = plt.subplots()
ax.fill_betweenx(y, x1, x2)
```

#### Screenshots or screencasts of the bug in action
![bokeh_area](https://user-images.githubusercontent.com/22548039/69818576-f0ed5700-11f4-11ea-912d-006ce09ac20a.PNG)

user: ghost
issue_9473: state: open
title: [FEATURE]Graceful handling of no data plots
body: Bokeh plots sometimes get handed no data. This is relevant in a dashboard type scenario where the queries are fixed and the results change each time you run the query. Or really any type of scenario where you are repeatedly generating plots from a changing data source.

Currently you would have to check if the data is empty and either produce a custom 'data empty' plot or insert some other placeholder.

It would be very nice if bokeh could degrade gracefully in this situation and perhaps have some sort of sensible "No Data" plot. It may also be useful to people in debugging situations who don't realize they are feeding bokeh empty data and just see a no axes blank result with little indicator of what went wrong.
user: shughes-uk
issue_9468: state: open
title: [BUG] CheckboxEditor BooleanFormatter cannot edit, not formatting
body: Similar to #9456 which also relates to editing data tables.

#### Software version info
bokeh 1.4.0
pandas 0.25.1
chrome  78.0.3904.97 (Official Build) (64-bit)
macOS 10.14.6 Mojave

#### Description of expected behavior and the observed behavior
Context: an editable table with 
- a boolean column, configured with a CheckboxEditor
- a second boolean column, configured with a BooleanFormatter

Expect: 
- can edit the value in the editable column
- values are formatted in the formatted column

Observed:
- double click in the editable column displays a checkbox, but cannot check or uncheck it
- the formatted column shows nothing

#### Complete, minimal, self-contained example code that reproduces the issue
```
from bokeh.models.widgets import DataTable, CheckboxEditor, BooleanFormatter, TableColumn
from bokeh.models import ColumnDataSource
from bokeh.plotting import show

table = DataTable(
    source=ColumnDataSource(dict(check1=[False, True], check2=[False, True])),
    columns=[
        TableColumn(
            field='check1',
            title='Check1',
            editor=CheckboxEditor(),
        ),
        TableColumn(
            field='check2',
            title='Check2',
            formatter=BooleanFormatter(icon='check'),
        ),
    ],             
    editable=True,
)

show(table)
```

#### Stack traceback and/or browser JavaScript console output

No errors showing in developer tools or python.

#### Screenshots

<img width="628" alt="Screenshot 2019-11-25 at 15 23 26" src="https://user-images.githubusercontent.com/31922007/69553662-71fbe280-0f98-11ea-9359-771b6dc0b1d3.png">
<img width="639" alt="Screenshot 2019-11-25 at 15 41 02" src="https://user-images.githubusercontent.com/31922007/69554680-09156a00-0f9a-11ea-8167-6ef6bf48ee4b.png">
<img width="616" alt="Screenshot 2019-11-25 at 15 39 54" src="https://user-images.githubusercontent.com/31922007/69554553-df5c4300-0f99-11ea-939e-f0202179de6a.png">

user: comatrion
issue_9456: state: open
title: [BUG] DateEditor DateFormatter Cannot read property 'name' of undefined
body: 
#### Software version info
bokeh 1.4.0
pandas 0.25.1
chrome  78.0.3904.97 (Official Build) (64-bit)
macOS 10.14.6 Mojave

#### Description of expected behavior and the observed behavior
Context: an editable table with a date column configured with a date editor and formatter
Expect: can edit a date in the date column
Observed: cannot edit date:
- double click on cell clears contents (should this display a date picker?)
- enter a date in the defined format
- hit return
- developer tools shows javascript error "Cannot read property 'name' of undefined"

#### Complete, minimal, self-contained example code that reproduces the issue
```
from bokeh.models.widgets import DataTable, DateEditor, DateFormatter, TableColumn
from bokeh.models import ColumnDataSource
from bokeh.plotting import show

import pandas as pd

table = DataTable(
    source=ColumnDataSource(dict(date=[pd.datetime.now()])),
    columns=[TableColumn(
        field='date',
        title='Date',
        formatter=DateFormatter(format="ATOM"),
        editor=DateEditor()
    )],
    editable=True,
)

show(table)
```

#### Stack traceback and/or browser JavaScript console output
```
Uncaught TypeError: Cannot read property 'name' of undefined
    at Object.convert (bokeh-1.4.0.min.js:402)
    at bokeh-1.4.0.min.js:402
    at e.doFormat (bokeh-tables-1.4.0.min.js:121)
    at makeActiveCellNormal (bokeh-tables-1.4.0.min.js:97)
    at updateRowCount (bokeh-tables-1.4.0.min.js:97)
    at SlickGrid.invalidate (bokeh-tables-1.4.0.min.js:97)
    at t.updateGrid (bokeh-tables-1.4.0.min.js:46)
    at t.<anonymous> (bokeh-tables-1.4.0.min.js:46)
    at n.emit (bokeh-1.4.0.min.js:266)
    at n.patch (bokeh-1.4.0.min.js:320)
```

#### Screenshots or screencasts of the bug in action

<img width="1167" alt="Screenshot 2019-11-23 at 14 16 29" src="https://user-images.githubusercontent.com/31922007/69480064-fbc27900-0dfb-11ea-922b-ce8d6021e5b8.png">
<img width="1168" alt="Screenshot 2019-11-23 at 14 16 12" src="https://user-images.githubusercontent.com/31922007/69480116-4e039a00-0dfc-11ea-8617-96515a9b7195.png">

user: comatrion
issue_9443: state: open
title: [FEATURE] Floodfill math functions
body: Hi, I'm trying to color the plot between two math functions, the area zone. I've asked about that in stackoverflow https://stackoverflow.com/questions/58950303/coloring-the-area-between-two-curves-with-bokeh.
It seems this is not avalaible with bokeh actually, and I think this feature is very important. I'm a math teacher and I'm making a web page with mathematical teory and examples. But I cannot to visualize the area about integrals without graphing the area between two or more math functions.
I like bokeh because it offers an easy way for the web readers to interact with the plots (zoom, movin' points, change colors and so on).
@bigreddot has suggested to me I make a petition about that here and it talks about a web page about to implement floodfill in html5 canvas http://www.williammalone.com/articles/html5-canvas-javascript-paint-bucket-tool/

Thanks

user: tofol
issue_9420: state: open
title: Allow to test individual files/modules in `node make test`
body: @mattpap Here are some things that would be really helpful

* `py.test -m codebase` includes the JS linter. Then the current `pre-commit` hook will catch issues there before a push

* ability to run a single specified JS file, e.g. `node make test <file>` Right now there appears to be no way to do anything except run the entire test suite, every time

* can `node make build` be parallelized?

user: bryevdv
issue_9416: state: open
title: Add ability to configure CORS
body: Over in dask/dask-labextension#96 we are having some trouble setting CORS headers. The way we are exposing our bokeh dashboard means that it may end up being on a different domain to the Jupyter Lab session that we are connecting from. This is resulting in CORS issues.

It seems that in tornado the headers for each route must be configured independently, there is no way to set default headers. We can set the headers on the routes we are creating ourselves but not on the ones created within Boke server. Therefore it would be great to be able to set some headers in Bokeh server which get added to all app routes by default.

We could potentially proxy our bokeh server as an alternative but that adds an extra layer of complexity we would rather avoid.

user: jacobtomlinson
issue_9412: state: open
title: Error on upgrading to Bokeh v1.4.0 related to custom class in module hierarchy.
body: Hi,

I've encountered an error with a custom class after upgrading to Bokeh v1.4.0. I’ve created a minimum example which replicates the problem. The code is below, which needs to be in packages organised as follows:

![cctest](https://user-images.githubusercontent.com/25136779/68809635-4279db80-0664-11ea-8518-74fc7ef07bf5.png)

Custom.py:

```
from bokeh.plotting import Figure
from bokeh.layouts import Column


class Custom(Column):

    __view_model__ = 'Column'
    __subtype__ = 'Custom'

    def __init__(self):

        super().__init__()

        plot = Figure(plot_height=500, plot_width=500)
        plot.circle([1, 2, 3], [3, 2, 1], size=20)

        self.children.append(plot)
```

example.py:

```
from bokeh.plotting import show
from cctest.Custom import Custom

custom = Custom()
show(custom)
```

(The init.py under both cctest and Examples is empty.)

The example runs under Bokeh v1.3.4 but fails under v1.4.0 with the following error:

**Error: Model ‘cctest.Custom.Column’ does not exist. This could be due to a widget or a custom model not being registered before first usage.**

Note that if all the code is in a single module it works fine (see the Discourse topic [here](https://discourse.bokeh.org/t/upgrade-to-bokeh-v1-4-needs-custom-model-registration/4222)) - the issue seems to be the module hierarchy.
user: marcuskd
issue_9408: state: open
title: [BUG] Spinner low/high update issue
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
bokeh/1.4.0
python/3.6
ubuntu/18.04

I encountered the issue first in firefox/70.0.1 (64-bit), but I couldn't reproduce it in chrome/78.0.3904.97 (Official Build) (64-bit)

#### Complete, minimal, self-contained example code that reproduces the issue
Steps to reproduce:
1) Run `bokeh serve` on a file with the following content:
```
from bokeh.io import curdoc
from bokeh.layouts import column
from bokeh.models import Spinner

low = 0
high = 10
step = 0.1

def high_spinner_callback(_attr, _old, new):
    low_spinner.high = new

high_spinner = Spinner(title='High:', low=low, value=high, step=step)
high_spinner.on_change('value', high_spinner_callback)

def low_spinner_callback(_attr, _old, new):
    high_spinner.low = new

low_spinner = Spinner(title='Low:', high=high, value=low, step=step)
low_spinner.on_change('value', low_spinner_callback)

curdoc().add_root(column(high_spinner, low_spinner))
```
2) Left click on the 'Low' spinner and enter a value >=10 from a keyboard. The shown value becomes the actual 10.
3) Left click on the down arrow of the 'High' spinner. The value won't change.
4) Left click on the down arrow of the 'Low' spinner. The value changes, but it makes 'High' spinner red. 

Expected behaviour: 'High' spinner stays OK

Also, the bug doesn't appear when doing the same, but mirrored for High/Low values.

#### Stack traceback and/or browser JavaScript console output
No

#### Screenshots or screencasts of the bug in action
![Peek 2019-11-13 16-50](https://user-images.githubusercontent.com/13196195/68780004-db453280-0635-11ea-8cb5-66608d700614.gif)

user: ivan-usov
issue_9407: state: open
title: Scalable Text
body: Users who make PNG exports at very high resolutions (e.g. for publication) run in to the issue that text font sizes are fixed and do not scale, leading to very tiny titles, labels, etc in the larger image. AFAIK units like `rem`, etc do not work on Canvas, but this should be verified. If not, it would be helpful to introduce our own unit that we manually scale according to canvas height. 

Tangentially related to https://github.com/bokeh/bokeh/issues/6840

cc @bokeh/dev for thoughts, input.
user: bryevdv
issue_9399: state: open
title: [BUG] PointDrawTool does not work for 'graph_renderer.node_renderer'
body: Hi,
the PointDrawTool does not work with an imported networkx graph. I am asking because I would like to interactively move the nodes of a networkx graph. 

My minimal code example is the following:
 ```
import networkx as nx

from bokeh.io import show, output_file
from bokeh.models import Plot, Range1d, MultiLine, Circle, HoverTool, BoxZoomTool, ResetTool, PointDrawTool
from bokeh.models.graphs import from_networkx
from bokeh.palettes import Spectral4

import bokeh.plotting as bpl

bpl.output_notebook()

# Prepare Data
G = nx.karate_club_graph()

SAME_CLUB_COLOR, DIFFERENT_CLUB_COLOR = "black", "red"
edge_attrs = {}

for start_node, end_node, _ in G.edges(data=True):
   edge_color = SAME_CLUB_COLOR if G.nodes[start_node]["club"] == G.nodes[end_node]["club"] else DIFFERENT_CLUB_COLOR
   edge_attrs[(start_node, end_node)] = edge_color

nx.set_edge_attributes(G, edge_attrs, "edge_color")

# Show with Bokeh
plot = Plot(plot_width=400, plot_height=400,
           x_range=Range1d(-1.1, 1.1), y_range=Range1d(-1.1, 1.1))
plot.title.text = "Graph Interaction Demonstration"

node_hover_tool = HoverTool(tooltips=[("index", "@index"), ("club", "@club")])

graph_renderer = from_networkx(G, nx.spring_layout, scale=1, center=(0, 0))

graph_renderer.node_renderer.glyph = Circle(size=15, fill_color=Spectral4[0])
graph_renderer.edge_renderer.glyph = MultiLine(line_color="edge_color", line_alpha=0.8, line_width=1)

plot.add_tools(node_hover_tool, BoxZoomTool(), ResetTool(), PointDrawTool(renderers = [graph_renderer.node_renderer], empty_value = 'black'))

plot.renderers.append(graph_renderer)

output_file("interactive_graphs.html")
show(plot)
```

Thanks.    

Already discussed [here](https://discourse.bokeh.org/t/networkx-import-and-pointdrawtool/3512?u=actionklaus).
user: FabiJa
issue_9398: state: open
title: Add support for legend_field with geo data
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
- bokeh version 1.4.0 
- python 3.7
- Windows 10


#### Description of expected behavior and the observed behavior
I want to add a legend to my plot of Geodata. In particular, I plot point data with different coloring according to a categorical variable in my data set. I transform my geopandas to to a GeoJsonDataSource accordingly. Everything works well, except creating the legend.

When I follow the following example: https://docs.bokeh.org/en/latest/docs/user_guide/annotations.html#legends
I get the following error: 
```
Column to be grouped does not exist in glyph data source.
```
Even though I include the source in the glyph method, i.e.
```python
p1.circle(
    "x",
    "y",
    source=geosource,
    fill_color={"field": "share_prot", "transform": color_mapper},
    line_color="black",
    line_alpha=0.5,
    line_width=0.3,
    alpha=0.6,
    #size=2,
    legend_group="share_prot"
)
```
#### Complete, minimal, self-contained example code that reproduces the issue

```python
from bokeh.io import output_file, show
from bokeh.models import GeoJSONDataSource
from bokeh.plotting import figure
from bokeh.sampledata.sample_geojson import geojson
import json

data = json.loads(geojson)
for i in range(len(data['features'])):
    data['features'][i]['properties']['Color'] = ['blue', 'red'][i%2]

geo_source = GeoJSONDataSource(geojson=json.dumps(data))
p = figure(background_fill_color="lightgrey")
p.circle(x='x', y='y', size=15, color='Color', alpha=0.7, source=geo_source, legend_group='Color')

show(p)
```

#### Stack traceback and/or browser JavaScript console output
```
<ipython-input-133-afa857e8c002> in <module>
     12 color_mapper = CategoricalColorMapper(factors=share_prot.unique(), palette=palette)
     13 p = figure(background_fill_color="lightgrey")
---> 14 p.circle(x='x', y='y', size=15, color='Color', alpha=0.7, source=geo_source, legend_group='Color')
     15 
     16 show(p)

fakesource in circle(self, x, y, **kwargs)

~\Anaconda3\envs\adv\lib\site-packages\bokeh\plotting\helpers.py in func(self, **kwargs)
    930 
    931         if legend_kwarg:
--> 932             _update_legend(self, legend_kwarg, glyph_renderer)
    933 
    934         self.renderers.append(glyph_renderer)

~\Anaconda3\envs\adv\lib\site-packages\bokeh\plotting\helpers.py in _update_legend(plot, legend_kwarg, glyph_renderer)
    487     kwarg, value = list(legend_kwarg.items())[0]
    488 
--> 489     _LEGEND_KWARG_HANDLERS[kwarg](value, legend, glyph_renderer)
    490 
    491 

~\Anaconda3\envs\adv\lib\site-packages\bokeh\plotting\helpers.py in _handle_legend_group(label, legend, glyph_renderer)
    454         raise ValueError("Cannot use 'legend_group' on a glyph without a data source already configured")
    455     if not (hasattr(source, 'column_names') and label in source.column_names):
--> 456         raise ValueError("Column to be grouped does not exist in glyph data source")
    457 
    458     column = source.data[label]

ValueError: Column to be grouped does not exist in glyph data source
```

user: raholler
issue_9378: state: open
title: [FEATURE] Make hover tooltip arrow style configurable
body: It's actually a very simple one, see image below:

![Screen Shot 2019-11-06 at 12 56 15 AM](https://user-images.githubusercontent.com/24261329/68275124-6ae45300-0030-11ea-9304-e2f3b78508c4.png)

Instead of the current style, won't it be better looking if we have the grey triangular head colored in white (with some transparency, too) just like the rest of the hovering tooltip? I've been looking for ways to change that color using css etc. but it's definitely better if the coloring can be done from an interface provided by the package.
user: allenfrostline
issue_9363: state: open
title: Reference Guide should link to Users Guide where appropriate.
body: E.g. the `Slope` reference guide entry could link to the "Adding Anotations" User Guide section for a more complete discussion. 
user: bryevdv
issue_9327: state: open
title: [BUG] Not possible to disable multi-select behavior for DataTable
body: The documentation for `DataTable` currently alludes to the possibility of disabling the multi-select behavior. However there is no documentation that provides guidance on how to modify/control this behavior. `DataTable` makes use of `SlickGrid` to render, and the [multi-select behavior](https://github.com/bokeh/SlickGrid/blob/bokeh/plugins/slick.rowselectionmodel.js#L98) is controlled by the option `grid.getOptions().multiSelect`.

The `DataTableView` is responsible for constructing/ownership of the grid. When the [grid options are constructed](https://github.com/bokeh/bokeh/blob/master/bokehjs/src/lib/models/widgets/tables/data_table.ts#L228) no value is passed for `multiSelect` making it impossible to control this feature without possibly resorting to custom JS event hacks.

I'd like to propose adding the option `multi_select: bool = True` to the `DataTable` constructor so that we can pass this option through to the view, and then provide it when constructing the `SlickGrid` instance.
user: mstump
issue_9296: state: open
title: Split bokehjs into multiple npm packages
body: This isn't a new idea, but I think it's high time to proceed with this, especially with 2.0 release on the horizon. Splitting up bokehjs means having clear separation between core, embed, utils, different subsets of models,  etc. on npm package level. We would keep all of those packages in a monorepo as we do now, and we would deploy them under `@bokeh` (or `@bokehjs`) namespace. We would also still provide `bokeh*.js` bundles as before. The immediate difference would be in improved project structure, improved build speeds, making it easier to use bokehjs as standalone npm library and allowing for more bundling options (e.g. to make domain specific bundles and reduce bundle sizes in general).

Note that this will require much more work than just introducing `lerna` or some other tool, and reorganizing the structure of the project. Currently the biggest issue is that the core of bokehjs depends on models, which prevents any separation between core and models. Fixing this will require redesigning some parts of the core, especially model serialization and deserialization, and handling of patching and streaming (making those work independently of `ColumnDataSource`). This and other changes will be useful on their own, because the current current design is fairly limited, especially when it comes to extensibility of  the library.
user: mattpap
issue_9294: state: open
title: [BUG] push_notebook doesn't work when LabelSet is only listener to a data source
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
bokeh: 1.3.4
python: 3.6.8
jupyterlab: 0.35.4
macos: 10.14.5
chrome: 76.0.3809.100 

#### Description of expected behavior and the observed behavior

push_notebook() correctly updates text labels when there's a listener such as scatter on the same data source as LabelSet. When the scatter is commented out, the data doesn't update.

#### Complete, minimal, self-contained example code that reproduces the issue

```
# code based on https://docs.bokeh.org/en/latest/docs/user_guide/annotations.html
# uncomment p.scatter line for correct behavior

from bokeh.plotting import figure, show, output_notebook
from bokeh.models import ColumnDataSource, Range1d, LabelSet, Label
output_notebook()

source = ColumnDataSource(data=dict(height=[],
                                    weight=[],
                                    names=[]))

p = figure(title='Dist. of 10th Grade Students at Lee High',
           x_range=Range1d(140, 275))
# p.scatter(x='weight', y='height', size=8, source=source)
p.xaxis[0].axis_label = 'Weight (lbs)'
p.yaxis[0].axis_label = 'Height (in)'

labels = LabelSet(x='weight', y='height', text='names', level='glyph',
              x_offset=5, y_offset=5, source=source, render_mode='canvas')

p.add_layout(labels)

h = show(p, notebook_handle=True)

source.data = {'height': [66, 71, 72, 68, 58, 62],
 'weight': [165, 189, 220, 141, 260, 174],
 'names': ['Mark', 'Amir', 'Matt', 'Greg', 'Owen', 'Juan']}
push_notebook(h)
```

#### Stack traceback and/or browser JavaScript console output

#### Screenshots or screencasts of the bug in action

user: currivan
issue_9283: state: open
title: [BUG] HoverTool: X axis hit testing too narrow with radius_dimension='y'
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
- bokeh version: 1.3.4
- python version: 3.7.4
- notebook version: 6.0.1
- JupyterLab version: 1.1.4
- OS version: MacOS 10.14.6
- Browser version: 
    * Google Chrome 77.0.3865.90 (Official Build) (64-bit)
    * Safari 13.0.1 (14608.2.11.1.11)

#### Description of expected behavior and the observed behavior
Hit testing of HoverTool does not work correctly with large aspect ratio and `radius_dimension='y'`.

#### Complete, minimal, self-contained example code that reproduces the issue

```python
# modified example in
# https://bokeh.pydata.org/en/latest/docs/user_guide/tools.html#basic-tooltips

from bokeh.plotting import figure, output_notebook, show, ColumnDataSource
from bokeh.layouts import Row
from bokeh import __version__ as bokeh_version
import sys
output_notebook()

print('python version:', sys.version_info)
print('bokeh version:', bokeh_version)

source = ColumnDataSource(data=dict(
    x_large=[i * 1e+4 for i in range(5)],
    x_small=[i * 1e-4 for i in range(5)],
    y=[2, 5, 8, 2, 7],
    desc=['A', 'b', 'C', 'd', 'E'],
))

TOOLTIPS = [("index", "$index"), ("(x,y)", "($x, $y)"), ("desc", "@desc"),]

p_large = figure(plot_width=400, plot_height=400, tooltips=TOOLTIPS,
           title="Too narrow hit?",)
p_large.circle('x_large', 'y', radius=.5, source=source, radius_dimension='y')

p_small= figure(plot_width=400, plot_height=400, tooltips=TOOLTIPS,
           title="seems to be ok",)
p_small.circle('x_small', 'y', radius=.5, source=source, radius_dimension='y')

show(Row(p_large, p_small))
```

#### Stack traceback and/or browser JavaScript console output
No error message produced.

#### Screenshots or screencasts of the bug in action
When X range is larger than Y range, hit area is to narrow ( in the X domain):
![large_ng](https://user-images.githubusercontent.com/12782884/66710496-ce25f100-edb4-11e9-99fb-a4aa9b142b93.png)
![large_ok](https://user-images.githubusercontent.com/12782884/66710497-d120e180-edb4-11e9-9dcb-45d51b3e80d1.png)


Seems to be ok when X range is smaller:
![small_ok](https://user-images.githubusercontent.com/12782884/66710481-6c658700-edb4-11e9-9f39-ed15f97ac846.png)

user: mtannaan
issue_9279: state: open
title: Add diagnostic / debugging info tool
body: 
![bokeh_logo](https://user-images.githubusercontent.com/5249529/66701743-0cada400-ed00-11e9-89f2-bbb7240fefdc.jpg)

Currently, the Bokeh logo in the Navigation bar has no tooltip.
Clicking on it opens the "https://bokeh.org/" page in a new tab.

This is confusing for new users that try to learn the meaning behind
the navigation symbols.

We discussed this at the PyConDE19 Bokeh sprint.

It may be better, if clicking or hovering the logo would display
a tool tip which offers a link to the documentation, but doesn't
immediately take the user away from the plot without confirmation.

This tool tip could also show the version number of the current Bokeh
installation.


user: mborus
issue_9254: state: open
title: [FEATURE] Add functionality to allow FactorRange.start/end to be writable.
body: For context: this issue being raised after an SO question kindly answered by @bryevdv: [here](https://stackoverflow.com/questions/58170511/how-to-specify-range-padding-that-takes-into-consideration-the-maximum-value-of).

Being able to set FactorRange.start/end can be useful when plotting categorical data, follows an example.

A ridge plot that gets clipped on top with `p.y_range.range_padding = 0.0`:

![image](https://user-images.githubusercontent.com/46817915/65962373-e7cb4e00-e42e-11e9-89ed-7d5a90d9c02d.png)

The same plot is not clipped but space is wasted with `p.y_range.range_padding = 0.2`

![image](https://user-images.githubusercontent.com/46817915/65962560-5ad4c480-e42f-11e9-8b03-58998d9bb93e.png)

Finally, by manually repositioning the image I get what I want:

![image](https://user-images.githubusercontent.com/46817915/65962604-717b1b80-e42f-11e9-9b51-47cf643319de.png)

Bokeh docs has a nice [example of a ridge plot](https://bokeh.pydata.org/en/latest/docs/gallery/ridgeplot.html) that can be used to depict the issue.

Thanks in advance.

user: gmagno
issue_9252: state: open
title: bokeh server: get client IP adress without embedding in Django/Flask/..[FEATURE]
body: In a bokeh server app i'd like to obtain the client IP adress from the HTTP request and depending on that adress adjust the returned content. Is this possible without embedding bokeh into django/flask/..?

Related [question on stackoverflow](https://stackoverflow.com/questions/58164261/bokeh-server-get-client-ip-adress-without-embedding-in-django-flask/58171475#58171475).
user: JorisMeertBambrugge
issue_9244: state: open
title: [BUG] GraphRenderer/GlyphRenderer/StaticLayoutProvider
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)

bokeh 1.3.4
Python 3.7.4
Linux
Firefox Dev / Chrome Dev

#### Description of expected behavior and the observed behavior

1. A GraphRenderer with a ```StaticLayoutProvider``` doesn't initially use any filter indices that may be defined with a ```CDSView``` etc. Screenshot 1 below shows this. The first plot is configured to filter out the first 5/10 points, the second plot to filter out the first 7/10. The second plot was created from a GraphRenderer, and it displays the points with the correct visual appearance (the first 5 points of the full set are red), but in the first 3 positions from the full dataset. 

   This seems to fix itself if I use the button in the example to remove and then reapply the same filter, so in this case it only affects the initial rendering. After poking around for some considerable time in BokehJS, I think the root cause is [this line](https://github.com/bokeh/bokeh/blob/e8b22ef161bae542ca06fe77cb12129ca7e3d870/bokehjs/src/lib/models/renderers/graph_renderer.ts#L69) in the GraphRenderer code. As a quick hack I modified my local copy of bokeh.js to take ```this.model.node_renderer.view.indices``` and set the ```_x``` and ```_y``` attributes on ```node_glyph``` based on those. After doing that the points appear correctly from the start. From looking at the code I think edges will be affected in the same way. 

2. While trying various combinations of things on the Python side to debug the first problem, I found another problem. If I create a ```GraphRenderer``` and then set a new ```node_renderer.glyph``` and ```node_renderer.data_source``` on it as [some of the docs demonstrate](https://bokeh.pydata.org/en/latest/docs/user_guide/graph.html#explicit-paths), it seems to cause some weird rendering behaviour.  Specifically, it doesn't update as usual after any filter change, so I have to click/pan on the plot to force a repaint. The initial state of the plot appears normal, but if I then remove the filter to force an update, it appears as shown in Screenshot 3. If I click/drag the mouse to make it redraw, it updates and becomes identical to the other plot, as it should have been in the first place. 

   If I modify ```node_renderer``` by assigning a new ```GlyphRenderer``` and configuring the same things (source, view, glyph) through its parameters in one shot, this doesn't happen (see example code).

#### Complete, minimal, self-contained example code that reproduces the issue

```python
import bokeh
from bokeh.layouts import row, column
from bokeh.models import ColumnDataSource, GlyphRenderer, GraphRenderer, StaticLayoutProvider, Circle, CDSView, IndexFilter, Button

from bokeh.plotting import figure, curdoc

POINTS = 10
testdata = {}
testdata['index'] = [i for i in range(POINTS)]
testdata['x'] = [i for i in range(POINTS)]
testdata['y'] = [i for i in range(POINTS)]
testdata['fill'] = ['#ff0000'] * (POINTS // 2) + ['#00ff00'] * (POINTS // 2)
layout = {i: (testdata['x'][i], testdata['y'][i]) for i in range(POINTS)}

# include points 5-9
indexfilter = IndexFilter(list(range(POINTS//2, POINTS, 1)))
# include points 7-9
indexfilter2 = IndexFilter(list(range(7, POINTS, 1)))
ds = ColumnDataSource(data=testdata)
v = CDSView(source=ds, filters=[indexfilter])

# this all works fine, just there for comparison
p_1 = figure(x_range=(-0.25, POINTS + .25), y_range=(-0.25, POINTS + .25), name='p_1')
r_1 = p_1.circle(x='x', y='y', size=15, fill_color='fill', line_color='#000000', source=ds, view=v)

p_2 = figure(x_range=(-0.25, POINTS + .25), y_range=(-0.25, POINTS + .25), name='p_2')
gr = GraphRenderer()
gr.layout_provider = StaticLayoutProvider(graph_layout=layout)

# this makes everything work as expected (+the BokehJS tweak)
gr.node_renderer = GlyphRenderer(data_source=ds, 
        glyph=Circle(x='x', y='y', size=15, line_color='#000000', fill_color='fill'), 
        view=CDSView(source=ds, filters=[indexfilter2]))

# configuring .node_renderer this way causes problem #2 
#gr.node_renderer.glyph = Circle(size=15, fill_color='fill', line_color='#000000')
#gr.node_renderer.data_source.data = ds.data 
#gr.node_renderer.view = CDSView(source=gr.node_renderer.data_source, filters=[indexfilter2])

p_2.renderers.append(gr)

def callback():
    r_1.view.filters = [] if len(r_1.view.filters) == 1 else [indexfilter]
    gr.node_renderer.view.filters = [] if len(gr.node_renderer.view.filters) == 1 else [indexfilter2]

button = Button(label="toggle")
button.on_click(callback)

curdoc().add_root(column(button, p_1, p_2))
```

#### Screenshots or screencasts of the bug in action
Screenshot 1 (initial state with filter indices being ignored when laying out lower plot):
![bug_](https://user-images.githubusercontent.com/9051013/65707892-530ace00-e085-11e9-8c35-7184b178a734.jpg)

Screenshot 2 (corrected initial state / expected filtered state)
![both_ok_](https://user-images.githubusercontent.com/9051013/65708045-982f0000-e085-11e9-932c-a497f1ed7192.jpg)

Screenshot 3 (after clicking toggle once)
![bug2](https://user-images.githubusercontent.com/9051013/65709061-dcbb9b00-e087-11e9-9bad-4eeb75f5e65f.jpg)

user: andrewramsay
issue_9236: state: open
title: [BUG] Updating values watched by a CommsHandle (in a notebook)
body: [Here's the post on the Discourse](https://discourse.bokeh.org/t/updating-values-watched-by-a-commshandle-in-a-notebook/3996).

### FOLLOW THESE INSTRUCTIONS CAREFULLY

*ISSUES THAT DO NOT CONTAIN NECESSARY INFORMATION MAY BE CLOSED, IMMEDIATELY*

The issue tracker is NOT the place for general support. For questions and technical assistance, come join the [Bokeh  Project Discourse](https://discourse.bokeh.org).

For defects or deficiencies, please provide *ALL OF THE FOLLOWING*:

#### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)

bokeh==1.3.4
ipywidgets==7.5.1
jupyter==1.0.0
jupyter-client==5.3.1
jupyter-console==6.0.0
jupyter-core==4.5.0
notebook==6.0.1

Python 3.7.3

Windows 10 Pro

Firefox 70.0b6

#### Description of expected behavior and the observed behavior

I am trying to animate a Bokeh plot inside a Layout which is itself contained within an object, all within a Jupyter notebook, using the stream interface and push_notebook. I would like to be able to dynamically update the dataset being plotted using a method, call it set_data. When I instantiate the object I start with an empty Layout, and when I set the dataset to be plotted, I generate a figure and set the Layout's children property to be a list containing just the figure. Below the Layout I have a couple of ipywidgets, a play button and a slider, along with some text. Pushing the play button or moving the slider should cause the frame value to advance, i.e., the ColumnDataSource powering the plot should update, and the plot display itself should update via push_notebook.

When I set the children property inside the set_data method, I can plot the initial (from the first frame) data points. The Layout changes (by adding children) and this propagates. Changes to the children of the layout don’t propagate: even though the ColumnDataSource is updated via stream (which can be verified by displaying some statistic like the median of the changed values) and the slider, which is jslinked to the play widget, advances, the plot itself doesn’t change when calling push_notebook.

On the other hand, if I set the Layout's children before I create the notebook handle, using show, everything works as expected.

##### Expected behavior

The plot's frames update when I press the Play button, as in the case when I create the handle after updating the children.

##### Actual behavior

The plot only shows the first frame and does not animate further.

#### Complete, minimal, self-contained example code that reproduces the issue

[Here's a link to the notebook containing a demo of this behavior](https://github.com/aliddell/misc-notebooks/blob/master/commshandle-demo.ipynb).

First cell, imports and context:

```python
from bokeh.models.sources import ColumnDataSource
from bokeh.plotting import figure
from bokeh.layouts import layout, row
from bokeh.io import output_notebook, show, push_notebook
from ipywidgets import Play, IntSlider, HBox, jslink, Label
import numpy as np

class DummyChange:
    """Replicates the relevant part of the ipywidget's change event"""
    def __init__(self, val):
        self.new = val

output_notebook()
```

Next cell, test data:

```python
np.random.seed(0)
data = {"x": np.random.rand(10),
        "y": np.random.rand(10),
        "size": 10*np.random.rand(10, 100)}
```

Next cell, dynamically updating the children property when setting data (this will not animate on pressing play):

```python
class Streamer:
    def __init__(self):
        self._data = None
        self._n_points = 0
        self._cds = ColumnDataSource(data=dict(x=[], y=[], size=[]))
        self._layout = layout([])  # Bokeh complains about an empty layout
        self._pw = Play(value=0,
                        min=0,
                        max=1,
                        step=1,
                        description="",
                        disabled=True)
        self._is = IntSlider(value=0,
                             min=0,
                             max=1,
                             step=1,
                             disabled=True)
        self._t = Label(value="0")
        jslink((self._is, "value"), (self._pw, "value"))
        self._pw.observe(self._animate, names="value")        

        self._handle = show(self._layout, notebook_handle=True)
        display(HBox([self._pw, self._is, self._t]))

    def _animate(self, change):
        frame = change.new
        self._cds.stream(new_data=dict(x=self._data["x"], # no change
                                       y=self._data["y"], # no change
                                       size=self._data["size"][:, frame]),
                         rollover=self._n_points)
        push_notebook(handle=self._handle)
        self._t.value = f"median size: {np.median(self._cds.data['size']):0.3f}"

    def set_data(self, data):
        self._data = data
        self._n_points = data["x"].size

        fig = figure(plot_width=800, plot_height=400)
        fig.scatter("x", "y", size="size", source=self._cds)
        self._layout.children = [row([fig])]

        # update player/slider widget values
        self._is.disabled = self._pw.disabled = False
        self._is.max = self._pw.max = self._data["size"].shape[1]

        self._animate(DummyChange(0))

s = Streamer()
s.set_data(data)
```

Next cell, creating the Layout with its children already in place (this **will** animate):

```python
class Streamer2:
    def __init__(self, data):
        self._data = data
        self._n_points = data["x"].size
        self._cds = ColumnDataSource(data=dict(x=[], y=[], size=[]))

        fig = figure(plot_width=800, plot_height=400)
        fig.scatter("x", "y", size="size", source=self._cds)

        self._layout = layout(row([fig]))
        self._pw = Play(value=0,
                        min=0,
                        max=self._data["size"].shape[1],
                        step=1,
                        description="",
                        disabled=False)
        self._is = IntSlider(value=0,
                             min=0,
                             max=self._data["size"].shape[1],
                             step=1,
                             disabled=False)
        self._t = Label(value="0")
        jslink((self._is, "value"), (self._pw, "value"))
        self._pw.observe(self._animate, names="value")        

        self._handle = show(self._layout, notebook_handle=True)
        display(HBox([self._pw, self._is, self._t]))
        
        self._animate(DummyChange(0))

    def _animate(self, change):
        frame = change.new
        self._cds.stream(new_data=dict(x=self._data["x"], # no change
                                       y=self._data["y"], # no change
                                       size=self._data["size"][:, frame]),
                         rollover=self._n_points)
        push_notebook(handle=self._handle)
        self._t.value = f"median size: {np.median(self._cds.data['size']):0.3f}"
```

#### Stack traceback and/or browser JavaScript console output

Nothing unusual in either JS or Jupyter consoles.

#### Screenshots or screencasts of the bug in action

![output](https://user-images.githubusercontent.com/844464/65431826-14a4b300-dde8-11e9-90a6-71a90a6b9c80.gif)


user: aliddell
issue_9233: state: open
title: Store selection per-glyph before aggregating
body: Currently when a selection tool acts on multiple glyphs, we try to combine those indices according to some policy. That's well and good, but we compute the aggregated versions "along the way" and never store the per-glyph selections.  I would like to make the steps along the way more explicit. In particular, I would like to make selections for each glyph be stored independently, possibly exposed to user. And then a policy can act cleanly on these independent selection values. 

I would also like to add a boolean flag per-glyph that states whether there is any selection at all. This solves the issue of knowing, e.g. just whether a `Line` or `Patch` is hit, but can also be uniformly applied across glyphs. 
user: bryevdv
issue_9231: state: open
title: [BUG] when using nginx in front of multiple bokeh server instances, an app may be loaded multiple times
body: ### FOLLOW THESE INSTRUCTIONS CAREFULLY

*ISSUES THAT DO NOT CONTAIN NECESSARY INFORMATION MAY BE CLOSED, IMMEDIATELY*

The issue tracker is NOT the place for general support. For questions and technical assistance, come join the [Bokeh  Project Discourse](https://discourse.bokeh.org).

For defects or deficiencies, please provide *ALL OF THE FOLLOWING*:

#### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
bokeh server version: 1.1.0
python version: 3.6.6

#### Description of expected behavior and the observed behavior
I have deployed a number of jpython notebooks that I load through a standalone bokeh server application.  I run multiple instance of the bokeh server with nginx in front.  Frequently, when I load one of the notebooks via an http call, the python code runs more than once.  The expectation is that it will only run once.  Running twice adds a performance hit, even though it eventually does work.

After discussion with @bryevdv at https://discourse.bokeh.org/t/using-standalone-bokeh-server-to-display-jupyter-notebooks-that-contains-some-holoview-objects/4015/6 , it seems this is a known issue.  

#### Complete, minimal, self-contained example code that reproduces the issue

```
# code goes here between backticks

```

#### Stack traceback and/or browser JavaScript console output

#### Screenshots or screencasts of the bug in action

user: chris-ratcliffe
issue_9223: state: open
title: [FEATURE] Make INLINE resources configurable for Sphinx
body: ### FOLLOW THESE INSTRUCTIONS CAREFULLY

*ISSUES THAT DO NOT CONTAIN NECESSARY INFORMATION MAY BE CLOSED, IMMEDIATELY*

The issue tracker is NOT the place for general support. For questions and technical assistance, come join the [Bokeh Project Discourse](https://discourse.bokeh.org).

**Is your feature request related to a problem? Please describe.**
Sphinx extension bokeh_plot directive resources defaulted to cdn which is an issue when working on computers that are not connected to the internet

**Describe the solution you'd like**
A workaround for cdn resources

**Describe alternatives you've considered**
Monkey patch on conf.py file as below:

```python
def get_sphinx_resources(include Bokehjs api-false):
    from bokeh.resources import INLINE
    return INLINE

import bokeh.sphinxext.util as util
util.get_sphinx_resources = get_sphinx_resources
```

**Additional context**
Add any other context or screenshots about the feature request here.

user: maxbsp3
issue_9221: state: open
title: DataTable with AjaxDataSource won't progmatically select row
body: Expected Behaviour: Programitcally selecting the row in a DataTable should scroll the row into view.  This works with a ColumnDataSource, but does not work with an AjaxDataSource.  Previous discussion of this issue can be found [here](https://discourse.bokeh.org/t/datatable-with-ajaxdatasource-scrolls-to-last-row-on-update/3973).  The specific behaviour that I ultimately want, is that when table data is updated, I want the last new data to be in the Users view, which means scrolling to the last row in the table. 

Observed Behaviour:  The row is not selected, the table does not scroll to row. 

bokeh version: 1.3.4
browser Chrome: Version 76.0.3809.132 (Official Build) (64-bit)
host OS: Ubuntu 18.04
python: Python 3.6.8 (default, Aug 20 2019, 17:12:48) [GCC 8.3.0] on linux

Minimal Code:
Because the issue is only seen with an AjaxDataSource, there are two pieces of code, one for the Flask server providing the data, and the other being the bokeh code.  Start the flask server in one terminal, and the bokeh code in another.  The minimal code supports testing both "append/replace" modes of the AjaxDataSource, as commented in the source.
Flask server:
```
#!/usr/bin/python
# -*- coding: utf-8 -*-
import time
from flask import Flask, jsonify
from flask_cors import CORS,cross_origin

# install cors: pip3 install -U flask-cors

app = Flask(__name__)
CORS(app, support_credentials=True)
PAGE_URL = "/test"
MODE = ["append", "replace"][1]  # change index 0/1 to get different modes

G = {  # globals...
    "table": {"a": []},
}

@app.route(PAGE_URL + "/table", methods=['POST', 'OPIONS'])
@cross_origin(supports_credentials=True)
def url__table_data():
    # Ajax URL, add a timestamp to the data stream, either whole table (replace) or last item (append)
    G["table"]["a"].append("{}".format(time.time()))
    if MODE == "append": return jsonify({"a": [G["table"]["a"][-1]]})
    else: return jsonify(G["table"])

# Ajax URL: http://127.0.0.1:6800/test/table
app.run(host="0.0.0.0", port=6800, debug=False)
```

The bopkeh code uses two different methods to try and select the last row of the DataTable.  One is a callback when the source data changes, and the other is thru a button that the user can press.  Neither method causes the last row to be selected.
The bokeh code:
```
#!/usr/bin/python
# -*- coding: utf-8 -*-
from bokeh.layouts import layout, column
from bokeh.models.widgets import Div, DataTable, TableColumn
from bokeh.models import AjaxDataSource, CustomJS
from bokeh.models.widgets.buttons import Button
from bokeh.io import show

MODE = ["append", "replace"][1]  # change index 0/1 to get different modes

source = AjaxDataSource(data=dict(a=[]), data_url="http://127.0.0.1:6800/test/table",
                        polling_interval=1000, mode=MODE)

# The GOAL of this callback is to scroll the DataTable to the last row,
# every time the datatable gets new data...
callback = CustomJS(args=dict(source=source),
                    code="""
                        var last_row_idx = source.get_length();
                        if (last_row_idx) source.selected.indices = [last_row_idx - 1];
                        //console.log("ads", cb_obj, source, (last_row_idx - 1));
                    """)
source.js_on_change("data", callback)

columns = [TableColumn(field=c) for c in source.data.keys()]
dt_table = DataTable(source=source, columns=columns, selectable=True, scroll_to_selection=True)

button = Button()
button.js_on_click(CustomJS(args=dict(source=source), code="""
    var last_row_idx = source.get_length();
    if (last_row_idx) source.selected.indices = [last_row_idx - 1];
    //console.log("Button", cb_obj, source, (last_row_idx - 1));
"""))

doc_layout = layout()
doc_layout.children.append(column(Div(text="""<h1>Hello World!</h1><p>Using an AjaxDataSource.</p>""")))
doc_layout.children.append(column(button, dt_table))

show(doc_layout)
```

Note that the issue does not occur with a ColumnDataSource, only with an AjaxDataSource.

I have used the `console` output in the above example to inspect the `source.selected.indices` and indeed this value is updated by the callbacks above (both button and the data change callback).  But it seems that this does not trigger the table to scroll.  Also note that I triedd adding a `source.selected.change.emit();` to manually trigger the table to scroll, but that also did nothing.

The workaround, as discussed in the above linked discussion, is to call SlickGrid directly, but I have yet to get to trying that out... (TODO).

user: sistemicorp
issue_9216: state: open
title: [BUG]RadioButtonGroup erroneous layout
body: Hi,
Info
------------------------------------------
bokeh = 1.3.4
python = 3.7.4
OS = linux (CentOS7.3) & Widnows 7
Browser = IE, Firefox, google Chrome


Issue
----------------------
I have some issues with RadioButtonGroup layout management, the following piece of code:
```
from bokeh.io import output_file, show
from bokeh.layouts import column,row
from bokeh.models.widgets import Button, RadioButtonGroup, Select, Slider

output_file("layout_widgets.html")

# create some widgets
slider = Slider(start=0, end=10, value=1, step=.1, title="Slider")
button_group1 = RadioButtonGroup(labels=["Option {:d}".format(i+1) for i in range(10)], active=0)
select = Select(title="Option:", value="foo", options=["foo", "bar", "baz", "quux"])
button_1 = Button(label="Button 1")
button_2 = Button(label="Button 2")

slider2 = Slider(start=0, end=10, value=1, step=.1, title="Slider")
button_group2 = RadioButtonGroup(labels=["Option {:d}".format(i+101) for i in range(10)], active=0)
select2 = Select(title="Option:", value="foo", options=["foo", "bar", "baz", "quux"])
button_11 = Button(label="Button 1")
button_12 = Button(label="Button 2")
show(row(column(button_1, slider, button_group1, select, button_2, width=300),column(button_11,slider2,button_group2, select2,button_12,width=300)))
```
with bokeh=1.3.4, I got
![image](https://user-images.githubusercontent.com/55091731/64532480-e2885100-d311-11e9-80c3-2ffff63b1071.png)


with bokeh=1.04, I got (as expected)
![image](https://user-images.githubusercontent.com/55091731/64532354-94734d80-d311-11e9-8832-7baefff4867c.png)

Is it a regression or a new feature ?
Best regards
LeDahu31


user: LeDahu31
issue_9197: state: open
title: [BUG] Parameter hover_line_width throws attribute error for segment glyph
body: #### software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
- bokeh 3.1.4
- python3.6
- ubuntu
- chrome


#### Description of expected behavior and the observed behavior
I want to create a segment that changes its line width when hovered over. I use the parameter `hover_line_width`. I get an attribute error "unexpected attribute", see below.


#### Complete, minimal, self-contained example code that reproduces the issue

```
from bokeh.plotting import figure, output_file, save
from bokeh.models import HoverTool

p = figure()
r = p.segment(x0=[1,2], y0=[2,3], x1=[10,20], y1=[20,30], line_width=3, hover_line_width=6)
p.add_tools(HoverTool(renderers=[r]))

output_file("hover.html")
save(p)
```

#### Stack traceback and/or browser JavaScript console output
Traceback (most recent call last):
  File "...", line 257, in <module>
    debug()
  File "...", line 248, in debug
    r = p.segment(x0=[1,2], y0=[2,3], x1=[10,20], y1=[20,30], line_width=3, hover_line_width=6)
  File "fakesource", line 7, in segment
  File "/usr/local/lib/python3.6/dist-packages/bokeh/plotting/helpers.py", line 857, in func
    glyph = _make_glyph(glyphclass, kwargs, glyph_ca)
  File "/usr/local/lib/python3.6/dist-packages/bokeh/plotting/helpers.py", line 398, in _make_glyph
    return glyphclass(**kws)
  File "/usr/local/lib/python3.6/dist-packages/bokeh/model.py", line 307, in __init__
    super(Model, self).__init__(**kwargs)
  File "/usr/local/lib/python3.6/dist-packages/bokeh/core/has_props.py", line 253, in __init__
    setattr(self, name, value)
  File "/usr/local/lib/python3.6/dist-packages/bokeh/core/has_props.py", line 288, in __setattr__
    (name, self.__class__.__name__, text, nice_join(matches)))
AttributeError: unexpected attribute 'hover_line_width' to Segment, similar attributes are line_width


user: hklarner
issue_9195: state: open
title: [FEATURE] Fail on bokeh.embed.file_html invalid template
body: **Is your feature request related to a problem? Please describe.**
Right now bokeh.embed.file_html fails silently if given invalid input (e.g. file string "./template.html") as input parameter and generates with the default bokeh.core.template.FILE template. This leads mostly to confusion.

**Describe the solution you'd like**
Throw an exception when the parameter for template cannot be used, instead of silently deleting it.

**Describe alternatives you've considered**
Possibly print a warning message stating that template was swapped to default, or at least support creation of Jinja2 Templates from file name strings

user: unc0mm0n
issue_9191: state: open
title: Integration tests unreliable again
body: Common offenders are:
```
Test_TextInput::test_server_on_change_round_trip FAILED [ 81%]
Test_DataTableCopyPaste::test_multi_row_copy FAILED [ 93%]
```
but there are other less frequent failures as well. It's interesting that sometimes they just pass, but there are PRs (e.g. gmaps) where dozens of job or build restarts can't clear them up.
user: mattpap
issue_9172: state: open
title: Axis bounds set on log scale axis cause chart to zoom/stretch when panning past bound limit
body: Setting limits on the log y axis and panning the chart does not stop panning at the bounds limit, below is code to reproduce (occurs in v1.3.4). The upper bound is respected and we can't pan past it, it is only the lower limit which causes the chart to stretch/zoom. 

```python
from bokeh.models import Range1d
from bokeh.plotting import figure, show

p = figure(x_range=(-8, 8), y_axis_type="log",
            plot_width=400, plot_height=300)
p.y_range = Range1d(10**-1, 10**5, bounds="auto")
p.triangle(np.random.randn(1000),
         np.random.randn(1000),
         size=np.random.uniform(2, 10, 1000),
         alpha=.5, angle=-1.57) # angle -pi/2 in radians
show(p)
```
user: sereeena
issue_9151: state: open
title: [BUG] FactorRange updates not processed correctly
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
bokeh.version: 1.3.0
python: 3.7.3
OS: Windows 10
browser: all

#### Description of expected behavior and the observed behavior
I am struggling to get the categorical bar chart for dynamic definitions of the factors working and the problem is related to the behavior of the xrange of the chart.

There are two ways to update the x_range of a chart:
  1:  p.x_range = FactorRange(factors=list(df['factors'].values))
  2:  p.x_range.factors = list(df['factors'].values)

In the first case a new x_range is created from a new FactorRange instance, in the second case only the factors are set.

For my specific implementation for some reason the first option does not work. I am left with an empty plot (no errors, not in Python, not in JS):
![image](https://user-images.githubusercontent.com/7702207/62769129-abb3e680-ba98-11e9-83e5-a63a7eb2a6c2.png)

If I use the second option (.x_range_.factors) the plot is populated partly as expected, but I get errors in the JS console and the sizing seems incorrect:
![image](https://user-images.githubusercontent.com/7702207/62769312-f5043600-ba98-11e9-975c-7ce8b0622cd5.png)
![image](https://user-images.githubusercontent.com/7702207/62769334-02b9bb80-ba99-11e9-8466-a3bf17b656cf.png)

After doing some more actions I also see this error (though I guess based on the statements this is a slickgrid issue, not a chart issue, so possibly irrelevant): 
![image](https://user-images.githubusercontent.com/7702207/62769581-93909700-ba99-11e9-84fb-74ba5ece3086.png)


I tried to reproduce both behaviours, but the first behavior I cannot reproduce outside of my app. (Any pointers for what could cause this are welcome).
The second behaviour I can reproduce with the example below. How can I workaround this issue? What is not handled if I just set .x_range.factors?

My expectation:
- just setting factors produces a correct output without errors on the JS side
- if possible some more console (JS or Python) output when there *is* data and definitions, but there is no plot

#### Complete, minimal, self-contained example code that reproduces the issue

```
from bokeh.plotting import curdoc, figure
import numpy as np
import pandas as pd
from bokeh.models import ColumnDataSource, Button, FactorRange
from bokeh.palettes import Spectral6
from bokeh.transform import factor_cmap
from bokeh.models.glyphs import VBar

df = pd.DataFrame(data=[['2000', 1, ('2000', '1')],['2001',2, ('2001', '2')]],
                    columns=['colors', 'value', 'factors'])

groups = df['colors'].unique()
index_cmap = factor_cmap('factors', palette=Spectral6, factors=groups, end=1)

factor_range = FactorRange(factors=list(df['factors'].values))
p = figure(plot_width=800, plot_height=300, title="Chart",
            x_range=factor_range,
            match_aspect=False, sizing_mode="stretch_width",
            name='chart')

vbar = VBar(x='factors', top='value', width=1,
                    line_color="white", fill_color=index_cmap)

p.y_range.start = 0
p.x_range.range_padding = 0.05
p.xgrid.grid_line_color = None
p.xaxis.axis_label = "value"
p.outline_line_color = None

source = ColumnDataSource(data=df)
bar_renderer = p.add_glyph(source, vbar)

def change_data():
    df = pd.DataFrame(data=[['2000', 3, ('2000')], ['2001', 5, ('2001')]],
                      columns=['colors', 'value', 'factors'])

    groups = df['colors'].unique()

    index_cmap = factor_cmap('factors', palette=Spectral6, factors=groups, end=1)
    vbar.fill_color = index_cmap
    source.data = df
    #p.x_range = FactorRange(factors=list(df['factors'].values))
    p.x_range.factors = list(df['factors'].values)

btn = Button(label="Change data")
btn.on_click(change_data)

curdoc().add_root(p)
curdoc().add_root(btn)
```

#### Stack traceback and/or browser JavaScript console output
See screenshot below

#### Screenshots or screencasts of the bug in action
![image](https://user-images.githubusercontent.com/7702207/62768419-182de600-ba97-11e9-9b1e-104cfb65f79d.png)

user: harmbuisman
issue_9132: state: open
title: [BUG/FEATURE] Category barplot does not handle non-string categories + missing error message
body: ## ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
bokeh.version = 1.3.0
python: 3.7.3
OS: Windows 10
browser: all

#### Description of expected behavior and the observed behavior
I ran into the issues when trying to adapt the Pandas example in https://bokeh.pydata.org/en/latest/docs/user_guide/categorical.html to my usecase

When running a category bar plot example on categories that are years I run into several issues that took me a lot of time to solve. I am wondering if this is a bug, but if not I like to request handling of non-string values by the method involved and better error reporting when updating data that is in a different format than what is expected.

##### Issue 1
index_cmap = factor_cmap('year', palette=Spectral6, factors=groups) gives the following error if groups is not integer:
Seq(String), Seq(Tuple(String, String)) or Seq(Tuple(String, String, String)), got array([2000, 2001], dtype=int64)

Expectation: I would expect factors to handle any data type, e.g. Integers as well

##### Issue 2
In the example below if I comment out .astype(str) in df['year'] = df['year']#.astype(str) then the example does not show any plot. 

Expectation: I would expect an error message in the console telling me what goes wrong.

##### Issue 3
If I leave in the code below the    #df['year'] = df['year'].astype(str) liine commented, then upon pressing the button the plot axes are updated, but the data in the plot is not. Even though I can verify that the data in the source is correctly set to the new data. The problem seems to by that the type of the column is not string. 

Expectation: I would expect an error message in the console telling me what goes wrong.

Result without string conversion:
![image](https://user-images.githubusercontent.com/7702207/62285629-4471b300-b456-11e9-88b5-ba56ee6637bc.png)

Expected result (shown when uncommenting):
![image](https://user-images.githubusercontent.com/7702207/62285840-bd710a80-b456-11e9-9d43-0465a243ccc0.png)


In summary my requests:
* handling of non-string factors
* better error messages when
- data versus configuration leads to not showing a plot at all
- updates triggered by data change do not lead to changed plots


#### Complete, minimal, self-contained example code that reproduces the issue

```
from bokeh.plotting import curdoc, figure
import numpy as np
import pandas as pd
from bokeh.models import ColumnDataSource, Button
from bokeh.palettes import Spectral6
from bokeh.transform import factor_cmap
from bokeh.models.glyphs import VBar

df = pd.DataFrame(data=[[2000, 1], [2001, 2]],
                  columns=['year', 'count'])

# to fix error:
# expected an element of either Seq(String), Seq(Tuple(String, String)) or Seq(Tuple(String, String, String)), got array([2000, 2001], dtype=int64)
df['year'] = df['year'].astype(str)

groups = df['year'].unique()
index_cmap = factor_cmap('year', palette=Spectral6, factors=groups)

p = figure(plot_width=600, plot_height=300, title="Test plot",
           x_range=groups)

vbar = VBar(x='year', top='count', width=1,
            line_color="white", fill_color=index_cmap)

source = ColumnDataSource(data=df)
bar_renderer = p.add_glyph(source, vbar)

def change_data():
    df = pd.DataFrame(data=[[2002, 5], [2003, 10], [2004, 10]],
            columns=['year', 'count'])
    
    # to fix: uncomment this
    #df['year'] = df['year'].astype(str)
    groups = list(map(str, sorted(df['year'].unique())))

    index_cmap = factor_cmap('year', palette=Spectral6, factors=groups)
    vbar.fill_color = index_cmap
    source.data = df
    p.x_range.factors = groups

btn = Button(label="Change data")
btn.on_click(change_data)

curdoc().add_root(p)
curdoc().add_root(btn)
```

#### Stack traceback and/or browser JavaScript console output
When not passing string for the categories in the first place I get:
expected an element of either Seq(String), Seq(Tuple(String, String)) or Seq(Tuple(String, String, String)), got array([2000, 2001], dtype=int64)

#### Screenshots or screencasts of the bug in action

user: harmbuisman
issue_9128: state: open
title: [FEATURE] make it easier to integrate widgets with css frameworks
body: **Is your feature request related to a problem? Please describe.**
It's hard to integrate bokeh with a css framework where you work by applying classes to html elements. When inserting a button with bokeh you get to chose from 4 types and there is an automatic default with colors. 
```
 Button(
            label="Toggle",
            button_type="primary",
            css_classes=["btn", "waves-effect", "waves-teal"],
        )
```
The issue with this is that your bokeh styles are overwriting some of the framework styles and I have to go manually and add some other styles. 

**Describe the solution you'd like**
I would like a fifth button type, call it empty, that has no styles attached to it, so when I use css_classes, those are the only styles applied to it. Also, I see that the classes are added to the parent div, I would like to also be able to target the button element with classes.
user: ClaudiuCreanga
issue_9124: state: open
title: [FEATURE] ImageIcon subclass for custom button appearance
body: **Describe the solution you'd like**
I would like a way to use custom `.png` images as icons within a bokeh Button widget. @bryevdv suggested in gitter that "an `ImageIcon` subclass of `AbstractIcon` would be useful to have and plug right in". This seems like a perfectly fine approach to me. He also mentioned [the example here](https://github.com/bokeh/bokeh/tree/master/examples/custom/font-awesome) where a subclass is defined to use [font awesome icons](https://fontawesome.com/icons?d=gallery) in this way. 

For my exact use case, I would like a clean and straight-forward way to create a button which contains only the icon, and no text. Ideally the icon could be rendered from a png image with transparency and at any size. Support for vector images (`svg` et al) would be great as well.
user: bSolt
issue_9111: state: open
title: Generate Bokeh specification from BokehJS
body: We need to be able to generate a  specification of all Bokeh models and properties (and their defaults), as well as all enum types, etc. There is currently `scripts/spec.py` which does this from Python but BokehJS should ultimately be used as the "ground truth". Also the current `spec.py` is not as helpful at describing property types as it could be (it just reprs the python types which can be overly complicated.)

cc @mattpap 

user: bryevdv
issue_9110: state: open
title: json_items and custom extensions
body: Noting that `json_items` is not currently useful with custom extensions. It outputs the JSON representation but there is currently no (easy) way to transmit or register the custom extension implementation. 

cc @mattpap 
user: bryevdv
issue_9087: state: open
title: Provide hit_filter property on HoverTool and TapTool
body: `HoverTool` (edit: and `TapTool`) should gain a new property:   
```
    hit_filter = Instance(SelectionFilter, help="""

    """)
```

A hit filter is a model with an `execute` method that accepts a `Selection` as well as a renderer model. To be used in the selection manager in the inspect codepath:
```
let hit_test_result = renderer_view.hit_test(geometry)
if (filter != null)
    hit_test_result = filter.execute(hit_test_result, renderer_view.model)
if (hit_test_result != null) {
......
``` 

The purpose of the hit filter is to be able to modify the initial raw hit test restult, i.e. by pruning `.indicies` down to only 1 result, etc. or it can return `null` if it wishes to cancel inspection results (no tooltips)

We will provide a `TopIndicesFilter` filter that prunes down to a specified N number of results, as well as a `CustomJSSelectionFilter` that users can implement arbitrary filtering with JS snippets. 
user: bryevdv
issue_9064: state: open
title: Add static typing to bokeh
body: This is a meta issue for adding static typing to bokeh:

- [x] `bokeh.palettes`
- [ ] `bokeh.embed.standalone`
- [ ] `bokeh.layouts`
- [x] `bokeh.util.paths`
user: mattpap
issue_9055: state: open
title: [BUG/FEATURE] Multiple tooltips showing for same patch
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
bokeh.version = 1.2.0
python: 3.7.3
OS: Windows 10
browser: all

#### Description of expected behavior and the observed behavior
I have a shapefile of the Netherlands provinces and when I define a tooltip I get in some locations of the map double, triple or even quadruple tooltips for the same region. This occurs mostly around the islands. 

I am not sure if the root cause is a Bokeh bug or an error in the shapefile (not sure how to validate the polygons of the shapefile), but I would like to have only one tooltip. So if this is not a Bokeh bug I would like to request the setting for HoverTool which can be set to show only the first tooltip for a glyphrenderer.

#### Complete, minimal, self-contained example code that reproduces the issue

```
import geopandas as gpd 
from bokeh.palettes import Reds
from bokeh.models import GeoJSONDataSource, LinearColorMapper, HoverTool
from bokeh.plotting import figure, show

geodf = gpd.read_file("provinces.geojson")

color_mapper = LinearColorMapper(palette=Reds[9][::-1])

p = figure()
geo_source = GeoJSONDataSource(geojson=geodf.to_json())
p.patches('xs', 'ys', fill_alpha=1, 
              fill_color={'field': 'index', 'transform': color_mapper}, 
              line_color='white', line_width=0.5, 
              source=geo_source)
hover = HoverTool()

p.add_tools(hover)
show(p)
```
To run the code, you need the following geojson file:
[provinces.zip](https://github.com/bokeh/bokeh/files/3198886/provinces.zip)

#### Stack traceback and/or browser JavaScript console output
No errors thrown

#### Screenshots or screencasts of the bug in action
![image](https://user-images.githubusercontent.com/7702207/61053070-123bdb00-a3ed-11e9-9149-9b2262f5d548.png)

user: harmbuisman
issue_9038: state: open
title: [BUG] Legend multi_line plot does not show if column is not present at definition time
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
bokeh.version = 1.2.0
python: 3.7.3
OS: Windows 10
browser: all

#### Description of expected behavior and the observed behavior
One can assing a datasource to a multi_line plot and in that definition point to the name of the x, y and legend columns. If at the moment of defining the multi_line the datasource does not have these columns, e.g. if you do not specify data for the datasource, then when doing an update of the data, the x and y *are* updated, but the legend columns is not updated with the new data. As a result you will see "labels" in the legend.

Expected: 
* legend in multi_line plot shows regardless of the legend column being in the datasource at definition time of the multi_line plot

#### Complete, minimal, self-contained example code that reproduces the issue

```
from bokeh.plotting import curdoc, figure
import numpy as np
from bokeh.models import ColumnDataSource

source = ColumnDataSource()

p = figure(width=600, height=300)
p.multi_line(xs='xs', ys='ys', legend='labels', source=source)

source.data = {'xs': [np.arange(5) * 1, np.arange(5) * 2],
        'ys': [np.ones(5) * 3, np.ones(5) * 4],
        'labels': ['one', 'two']}

curdoc().add_root(p)
```

#### Stack traceback and/or browser JavaScript console output
No relevant console output

#### Screenshots or screencasts of the bug in action
![image](https://user-images.githubusercontent.com/7702207/60446350-7a3a3680-9c21-11e9-8794-691157a7b513.png)

user: harmbuisman
issue_9036: state: open
title: [FEATURE] New Graph Selection/Inspection Policy: Node, Linked Edges, and Linked Nodes
body: I am currently using the `NodesAndLinkedEdges()` policy for my graph inspection policy and selection policy. Currently, a node is selected, and it becomes highlighted along with every edge leading in or out of that node. **However, I want every node attached to the highlighted edges to also be highlighted.**  

If someone could help clarify where exactly the NodesAndLinkedEdges() class is actually implemented, I would consider doing this myself, as my feature request is an extension from that class. 

It could be called `NodesLinkedEdgesandLinkedNodes()`. 

user: jareducherek
issue_9026: state: open
title: [FEATURE] Ability to close sessions and removing documents from BokehJS
body: Some context available here: https://discourse.bokeh.org/t/closing-bokeh-session-without-closing-the-browser/2883

Personally, I've found the need for it while using Bokeh in a web application where a user can open and close dialogs with Bokeh plots. The issue is that even if the dialog is closed (i.e. the node is completely removed from the DOM tree), the document is still there in `Bokeh.documents` and the WS connection is still live.

Right now, the only thing one could do is to use something like this (assuming there's only one document):
```
Bokeh.documents[0].event_manager.session.close();
Bokeh.documents.length = 0;
```
However, it will only close the connection, the document itself will still be referenced from an object that's not available publicly: https://github.com/bokeh/bokeh/blob/master/bokehjs/src/lib/embed/server.ts#L31
Maybe there are other references as well.
user: p-himik
issue_9020: state: open
title: Spinner's Button not displaying on cell selected of Data Table[BUG]
body: I add an option NumberEditor into a TableColumn of my Data Table. But there isn't a spinner's button to control value of cell selected.

```
source_bezier = ColumnDataSource({‘x’: [200, 100, 300], ‘y’: [100, 150, 150], ‘size’:[5,9,10],‘color’: [‘red’, ‘green’, ‘yellow’]})
columns = [TableColumn(field=“x”, title=“x”),
TableColumn(field=“y”, title=“y”),
TableColumn(field=“size”, title=“size”,editor=NumberEditor(step=0.1)) ,
TableColumn(field=‘color’, title=‘color’)]
table = DataTable(source=source_bezier, columns=columns, editable=True, height=200) 

```

![image](https://user-images.githubusercontent.com/40625637/59936376-22951180-9450-11e9-9155-339a8b4fc1fe.png)

user: duynguyen2411
issue_9018: state: open
title: Bokeh Server modify_doc in thread executor
body: It seems like there is no reason the `modify_doc` step could not be moved to an executor, so that the server as whole does not block during document initialization.

user: bryevdv
issue_9015: state: open
title: Allow slider to work on reverse range
body: Hi,

Sliders work on range which increases from start range to end range. Example: `Start 1 - End 10, Start -10 - End 1`. For ranges which decrease from start to end, slider doesn't work. 

Example: `Start 10 - End 1`

The slider value changes to NaN when using a decreasing range. 

![image](https://user-images.githubusercontent.com/22864480/59797686-2fb9d100-9295-11e9-956d-fa5fee1c8ebc.png)


`step = -1` doesn't work as well.

<img width="757" alt="image" src="https://user-images.githubusercontent.com/22864480/59797728-495b1880-9295-11e9-97af-63ad5df008a2.png">


Any solution would be appreciated. 

Thank you.
user: samirak93
issue_9012: state: open
title: [BUG]RangeSlider does not handle integer precision
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
bokeh.__version__ = 1.1.0
python: 3.7.3
OS: Windows 10
browser: all

#### Description of expected behavior and the observed behavior
The value of slider does not have integer precision when I use step=1

E.g. I get values that are 8.99999999999 instead of expected 9

I understand that float precision may not be the exact integer, but I do see 9 in the UI, so I expect to get 9 or at least 9.0 as the result. 

Potentially this needs an argument to set the slider as integer instead of float?

#### Complete, minimal, self-contained example code that reproduces the issue
```
# run the following
from bokeh.models.widgets import RangeSlider
from bokeh.layouts import layout
from bokeh.plotting import curdoc

slider = RangeSlider(title="Value",start=0, end=95, step=1, value=(0, 95))

def print_slider(attr, old, new):
    print(attr, old, new)

slider.on_change('value', print_slider)

curdoc().add_root(slider)
```

#### Stack traceback and/or browser JavaScript console output
value (0, 95) (1, 95)
value (1, 95) (4, 95)
value (4, 95) (6, 95)
value (6, 95) (7, 95)
value (7, 95) (8, 95)
value (8, 95) (8.999999999999998, 95)
value (8.999999999999998, 95) (9.999999999999998, 95)
value (9.999999999999998, 95) (10.999999999999998, 95)
value (10.999999999999998, 95) (12, 95)


#### Screenshots or screencasts of the bug in action
<img width="849" alt="bokeh_range_slider_precision" src="https://user-images.githubusercontent.com/7702207/59688956-03dc1400-91df-11e9-8bdf-ecb59b690766.PNG">
user: harmbuisman
issue_8929: state: open
title: [FEATURE] DatetimeAxis: Show bolded ticks for start of date
body: Check out this plot from the docs:

![](https://i.imgur.com/gvrIfiF.jpg)

It would be nice if the ticks for the start of date (11/15) would be bolder and more prominent than hours inside that date.
user: cool-RR
issue_8913: state: open
title: [FEATURE] Rescale axis on range_tool update/move
body: It would be great to rescale Y axis for example on the move of range_tool.
This should be an optional behaviour and should help in the cases when one observes local features (like peaks for example) within running window.
Here is an example in another platform: https://www.highcharts.com/demo/dynamic-master-detail/grid-light (select couple regions and you will notice Y axis limits and ticks being updated).
Another example is here: https://rstudio.github.io/dygraphs/gallery-range-selector.html
user: thoth291
issue_8888: state: open
title: [DOCS] Extend dev docs related to running the examples tests
body: While onboarding as a contributor, I ran into various issues when attempting to run the examples tests. Specifically:
* It wasn't explicit that I need to have Chrome installed locally
* I need to have chromedriver installed as well
* I need to start Chrome w/ a debugging port from the command line
* I need to build the bokehjs examples
* To update an example's baseline, I need to commit the changes to git 
user: canavandl
issue_8866: state: open
title: Div widget overlapping after layout overhaul in 1.1.0
body: After new layout overhaul in bokeh version 1.1.0 `Div` widgets are not responding in the layout as expected.

Before version 1.1.0 - the layout looked like this:
![image](https://user-images.githubusercontent.com/9287000/56600190-5faa6800-65b5-11e9-8d3e-204bc6063aa0.png)

Now the same layout looks like this:
![image](https://user-images.githubusercontent.com/9287000/56600258-836dae00-65b5-11e9-91e2-335700ded1f1.png)

If you force a different sizing mode other than fixed (e.g. 'stretch_both') then you at least avoid the overlapping, but the sizing still seems off. No matter what I put in the height and width when it's fixed, it doesn't seem to change size.
![image](https://user-images.githubusercontent.com/9287000/56600456-f414ca80-65b5-11e9-9c0e-69ca1cdec7ff.png)

I'm on windows, using a Chrome browser.

Similar code snippet to what I'm using in my app: 
```
div_header = Div(text="""
                    <h4><i>Stochastic Simulation Inputs</i></h4>""",
                                 style={
                                            'background-color': '#c8cfd6', 
                                            'outline': 'black solid thin',
                                            'text-align': 'center'
                                            }, height=15, width=610, 
                                 sizing_mode='stretch_both')

sim_duration = TextInput(title="Stochastic Simulation Length (hrs):", value="504", name='SimDuration')

layout = column(div_header, sim_duration)
show(layout)
```

user: pdmich
issue_8865: state: open
title: [BUG] Updating title object in bokeh server does not work
body: I am not able to change the title text of a figure interactively. In the minimal working example below, the title stays "old title" even after clicking the button. In the non-interactive version, this works fine.

Bokeh version 1.1.0.
Running on Windows 10, Anaconda environment, `bokeh serve --show` opens Firefox browser.

```

from bokeh.models.annotations import Title
from bokeh.plotting import figure, show
from bokeh.models.widgets import Button
from bokeh.layouts import row
import numpy as np
from bokeh.io import curdoc
x = np.arange(0, 2*np.pi, np.pi/100)
y = np.sin(x)
p = figure(title="old title")
p.circle(x, y)

button = Button(label="Update available curves", button_type="success")
def on_button():
  t = Title()
  t.text = 'new title'
  p.title = t
  print("Clicked")
button.on_click(on_button)

curdoc().add_root(row(p, button))
```

The title text stays the same. No error / warning shown in the console. "Clicked" is printed, so the function is definitely executed.

user: kasuteru
issue_8856: state: open
title: [BUG] Logging configuration when running bokeh server
body: In Bokeh 1.1.0, when a bokeh application is run on a bokeh server, configuration of the root logger inside the user application via `logging.basicConfig` or `bokeh.utils.logconfig.basicConfig` is ignored. This is because `basicConfig` has already been called when the bokeh server is started and has already assigned a handler to the root logger. The `logging.basicConfig` function does nothing when a handler is already assigned, which is expected behavior.  Documentation on the bokeh server page could help a user to know that to modify the root logger behavior within a program, the root logger handlers must be accessed directly. 
user: lunarspectrum
issue_8855: state: open
title: [FEATURE] Merged tickers
body: **Is your feature request related to a problem? Please describe.**

I'm plotting a time series.  I'd like the x axis (dates) to display evenly spaced ticks (one per year, then one per month at closer zoom levels, etc.), *plus* fixed ticks, one under data point.  This would allow readers to know exactly which date each point corresponds to.

**Describe the solution you'd like**

`CompositeTicker` is very close to what I need, but it requires each ticker to operate at different scale.  I'd like a `MergedTicker` that combines two or more tickers; I'd use `DatetimeTicker` and a `FixedTicker`.

**Describe alternatives you've considered**
I can use a `FixedTicker`, but I lose the smooth zooming behavior of `DatetimeTicker`.

Thanks for the hard work!
user: cpitclaudel
issue_8842: state: open
title: [BUG] Axis tick labels cut off when fixed_location is used
body: #### Software version info
Bokeh - 1.1.0
Python - 3.7.0
Chrome - 73.0.3683.103 (Windows 10)

#### Description of expected behavior and the observed behavior
The tick labels on the edges of the plot should be fully visible, not cut off when fixed_location is used. Tick labels should have consistent behaviour, regardless of the axis position.
![chrome_2019-04-15_14-47-55](https://user-images.githubusercontent.com/29014804/56108227-155e3100-5f8e-11e9-9967-33005352d532.png)
I'd also like to point out that this issue is also somewhat present even when fixed_location isn't used if the toolbar is hidden.
![chrome_2019-04-15_14-55-57](https://user-images.githubusercontent.com/29014804/56108358-dd0b2280-5f8e-11e9-81b4-0ffce439076b.png)
![chrome_2019-04-15_14-55-41](https://user-images.githubusercontent.com/29014804/56108361-e09ea980-5f8e-11e9-886f-bf03b3e70dbf.png)

#### Complete, minimal, self-contained example code that reproduces the issue

```python
from bokeh.io import show
from bokeh.plotting import figure

plot = figure(x_range=(0, 100), y_range=(-100, 100), plot_width=800, plot_height=200)
plot.circle([10, 20, 30, 40, 50, 60, 70, 80, 90], [-60, 70, 20, -40, 50, 80, 60, -30, -80],
            size=20, color="navy")
plot.xaxis.fixed_location = 0

show(plot)
```

user: JAK0723
issue_8841: state: open
title: [FEATURE] Interactive Legend: More mode options
body: When there are many lines glyphs in one plot, it is hard to discern them. We can already highlight the glyphs making use of the `click_policy` and `muted_glyph`. However clicking through a long list of legend entries is tidious and it would be much more elegant to hover over them. 

When hovering over legend entries, the corresponding glyph should be highlighted. @birdsarah  has provided [an example](https://c3js.org/samples/simple_multiple.html) in a[ previous request](https://github.com/bokeh/bokeh/issues/2274#issuecomment-101951256).



user: wochner
issue_8834: state: open
title: Update Layout Documentation
body: Both `'stretch_height'` and `'stretch_both'` sizing_modes don't actually fill the whole screen whatever I do in the released bokeh 1.1.0 whatever I do. This worked for most of the release candidates so I'll try to track down at what point the regression occurred.

bokeh 1.1.0
Python 3.6
OS: OSX 10.13.4
Browser: Chrome 73.0.3683.103

```
from bokeh.plotting import figure, Row, output_file, show

output_file('test.html')

p1 = figure(sizing_mode='stretch_both')
p1.scatter([1, 2, 3], [1, 2, 3])

p2 = figure(sizing_mode='stretch_both')
p2.scatter([1, 2, 3], [1, 2, 3])

show(Row(p1, p2))
```

![Screen Shot 2019-04-12 at 3 00 30 PM](https://user-images.githubusercontent.com/1550771/56042789-c5842d80-5d33-11e9-9fcd-60ab41363945.png)


user: philippjfr
issue_8833: state: open
title: [BUG] Unresponsive tools in Chrome (Windows)
body: #### Software version info
Bokeh - 1.1.0 (also tested on 1.0.4)
Python - 2.7.13
Chrome - 73.0.3683.86 (Windows 10)

#### Description of expected behavior and the observed behavior
Adding hovertool to plot with certain glyphs (e.g. quad, segment) causes figure toolbar to become completely unresponsive. Console doesn't show anything amiss. Can provide screencast if needed. 

Have tested on multiple computers (all Win10) - issue occurs consistently in Chrome but not in Firefox. Possibly related to #8150?

#### Complete, minimal, self-contained example code that reproduces the issue
```
from bokeh.plotting import figure, output_file, show
from bokeh.models import ColumnDataSource, WheelZoomTool, BoxZoomTool, ResetTool, HoverTool

# Generate data
x0_vals = [0, 2, 3, 5, 7, 8]
x1_vals = [1, 3, 4, 7, 8, 9]
y0_vals = [0, 0, 3, 3, 4, 6]
y1_vals = [0, 3, 3, 4, 6, 6]

# Make sources
src_xy = ColumnDataSource({'X0': x0_vals, 'X1': x1_vals, 'Y0': y0_vals, 'Y1': y1_vals})

# Create figure
tools = [WheelZoomTool(), BoxZoomTool(), ResetTool()]
p = figure(
    height=800, width=800,
    title='Test', y_axis_label='Y Value', x_axis_label='X Value',
    tools=tools
)

# Add renderers (in draw order - last on top).
quads = p.quad(
    top='Y1', bottom='Y0', left='X0', right='X1',
    source=src_xy,
    alpha=0.1, fill_color='grey', line_color='grey', line_dash='dotted',
    hover_fill_alpha=0.3, hover_fill_color='grey', hover_line_color='grey'
)

lines = p.segment(
    x0='X0', x1='X1',
    y0='Y0', y1='Y1',
    source=src_xy, 
    color='blue', line_width=2, line_dash='solid'
)

### Adding this hover tool will make the other tools totally unresponsive.
p.add_tools(HoverTool(renderers=[quads]))

# Show results.
output_file('test.html')
show(p)
```
user: xant-tv
issue_8831: state: open
title: [BUG] MultiPolygons doesn't work with GMapPlot
body: The MultiPolygons glyph doesn't seem to work in combination with GMapPlots. The code below shows the problem, as well as shows that the problem doesn't exist when I used Patches instead of GMapPlot or when I use MultiPolygons in a regular Plot.

When I run the code in Jupyter, I get no error in the notebook itself - the plot simply fails to show. I do get the following code in the web console:

<img width="667" alt="image" src="https://user-images.githubusercontent.com/2266682/55977232-45d75f80-5c5c-11e9-927f-23aa177cf4ea.png">


# System info:
Python 3.6.7 | packaged by conda-forge | (default, Feb 28 2019, 09:07:38) [GCC 7.3.0]
Bokeh 1.1.0
Problems apparent in both Chrome and Firefox, in JupyterLab, regular Jupyter notebook, and standalone webpage (opening from file).


# Code:

```
from numpy import mean
from bokeh.io import show, output_notebook
from bokeh.models.glyphs import MultiPolygons, Patches
from bokeh.models import GMapPlot, GMapOptions, ColumnDataSource, Range1d, Plot

multipolygons_data = {
 'x': [
    [[[-78.75412, -78.75412, -78.752747, -78.752747]]],
    [[[-78.759613, -78.759613, -78.75824, -78.75824]]],
    [[[-78.756866, -78.756866, -78.755493, -78.755493]]],
    [[[-78.756866, -78.756866, -78.755493, -78.755493]]],
    [[[-78.755493, -78.755493, -78.75412, -78.75412]]],
    [[[-78.759613, -78.759613, -78.75824, -78.75824]]],
    [[[-78.755493, -78.755493, -78.75412, -78.75412]]],
    [[[-78.760986, -78.760986, -78.759613, -78.759613]]],
    [[[-78.755493, -78.755493, -78.75412, -78.75412]]],
    [[[-78.75412, -78.75412, -78.752747, -78.752747]]],
    [[[-78.75412, -78.75412, -78.752747, -78.752747]]],
    [[[-78.75824, -78.75824, -78.756866, -78.756866]]]
 ],
 'y': [
    [[[35.741272, 35.739899, 35.739899, 35.741272]]],
    [[[35.741272, 35.739899, 35.739899, 35.741272]]],
    [[[35.741272, 35.739899, 35.739899, 35.741272]]],
    [[[35.739899, 35.738525, 35.738525, 35.739899]]],
    [[[35.741272, 35.739899, 35.739899, 35.741272]]],
    [[[35.739899, 35.738525, 35.738525, 35.739899]]],
    [[[35.739899, 35.738525, 35.738525, 35.739899]]],
    [[[35.741272, 35.739899, 35.739899, 35.741272]]],
    [[[35.738525, 35.737152, 35.737152, 35.738525]]],
    [[[35.739899, 35.738525, 35.738525, 35.739899]]],
    [[[35.738525, 35.737152, 35.737152, 35.738525]]],
    [[[35.739899, 35.738525, 35.738525, 35.739899]]]
 ]
}

patches_data = {k: [v2[0][0] for v2 in v] for k, v in multipolygons_data.items()}

# Setup for GMapPlot
lng_center = mean([d for a in multipolygons_data['x'] for b in a for c in b for d in c])
lat_center = mean([d for a in multipolygons_data['y'] for b in a for c in b for d in c])
zoom = 15
plot_height=350
map_type = 'hybrid'
map_options = GMapOptions(lat=lat_center, lng=lng_center, map_type=map_type, zoom=zoom)

output_notebook()

# MultiPolygons in GMapPlot - THIS DOES NOT WORK
plot1 = GMapPlot(
    x_range=Range1d(),
    y_range=Range1d(),
    map_options=map_options,
    plot_width=plot_width,
    plot_height=plot_height
)
plot1.api_key = API_KEY
model_object = MultiPolygons(xs='x', ys='y', name='test')
source = ColumnDataSource(multipolygons_data)
rend = plot1.add_glyph(source, model_object)
show(plot1)

# Patches in GMapPlot - THIS DOES WORK
plot2 = GMapPlot(
    x_range=Range1d(),
    y_range=Range1d(),
    map_options=map_options,
    plot_width=plot_width,
    plot_height=plot_height
)
plot2.api_key = API_KEY
model_object = Patches(xs='x', ys='y', name='test')
source = ColumnDataSource(patches_data)
rend = plot2.add_glyph(source, model_object)
show(plot2)

# MultiPolygons in regular Plot - THIS DOES WORK
plot3 = Plot(
    plot_width=plot_width,
    plot_height=plot_height
)
model_object = MultiPolygons(xs='x', ys='y', name='test')
source = ColumnDataSource(multipolygons_data)
rend = plot3.add_glyph(source, model_object)
show(plot3)

```

user: schaunwheeler
issue_8830: state: open
title: [FEATURE] Tree widget
body: I would like to request adding a tree widget to bokeh. 

I'm trying to build a dashboard / SQL editor tool with bokeh. 
A key ingredient in a editor is the side menu with expandable databases, table names etc.

Currently I tried to use flexx (has a TreeWidget), to which I can import my bokeh figures via BokehWidget. However, flexx lacks a TextAreaWidget and a DataTable.

Would you consider adding a tree widget of some sort to a future release of bokeh? I believe it can be quite handy in a number of useccases.


Thanks,
Eli
user: h5rdly
issue_8798: state: open
title: Using BACKSPACE for Edit Tool in Firefox on Windows not always working properly
body: Somehow edit tools, for example BoxEditTool, do not work properly in Firefox on Windows:  when trying to remove a box using BACKSPACE key after selecting it, Firefox instead goes back in history.  Even when cursor is in the plot.  That does not happen in Firefox on Linux.

#### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
OS: Windows 10, version 18.09
Browser: Firefox 66.0.2

#### Description of expected behavior and the observed behavior
I loaded the https://bokeh.pydata.org/en/latest/docs/user_guide/tools.html page and then clicked on the 'Edit Tolls' shortcut ( https://bokeh.pydata.org/en/latest/docs/user_guide/tools.html#userguide-tools-edit ), scrolled down to the BoxEditTool integrated example.  Then, I selected one of the boxes, and pressed BACKSPACE.  I expected the box to be removed, but instead the top of the page was shown.

#### Screenshots or screencasts of the bug in action

![screencast](https://user-images.githubusercontent.com/3921196/55186174-f3e80180-51a6-11e9-9d0b-cfd4977ac9c1.gif)

user: nasedil
issue_8797: state: open
title: Vertical slider changes height on interaction
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
```
Python version      :  3.6.7 | packaged by conda-forge | (default, Nov 20 2018, 18:37:09) 
IPython version     :  7.2.0
Tornado version     :  5.1.1
Bokeh version       :  1.1.0dev11-2-g51396fd
BokehJS static path :  /Users/jsignell/bokeh/bokeh/server/static
node.js version     :  v11.6.0
npm version         :  6.5.0
```

#### Description of expected behavior and the observed behavior
When orientation of sliders is vertical, the slider grows in height by about 50px on first interaction. This makes it extend outside of the allotted area in a notebook. The same behavior can be seen in static html - but is less noticeable since the boundaries are less defined. 

#### Complete, minimal, self-contained example code that reproduces the issue

```python
import bokeh
from bokeh.io import show

show(bokeh.models.widgets.Slider(value=2, start=1, end=10, 
                                 orientation='vertical', background='#00ff00'))
```

#### Screenshots or screencasts of the bug in action

### In the notebook
<img width="801" alt="Screen Shot 2019-03-27 at 11 00 05 AM" src="https://user-images.githubusercontent.com/4806877/55087754-f6e8d200-5080-11e9-8a05-acd54076392e.png">

### Setting height
<img width="829" alt="Screen Shot 2019-03-27 at 11 10 04 AM" src="https://user-images.githubusercontent.com/4806877/55087774-ff410d00-5080-11e9-988e-a35b4bea293e.png">

### In html with background
<img width="53" alt="Screen Shot 2019-03-27 at 11 19 02 AM" src="https://user-images.githubusercontent.com/4806877/55088486-306e0d00-5082-11e9-8864-876195d041de.png">

user: jsignell
issue_8792: state: open
title: Suggestion: Option to trigger HoverTool only on hit
body: #### Description of expected behavior and the observed behavior
Following the discussion on #8787, I would like the ability to only trigger hover callbacks on specific glyphs. Currently, the callback is triggered on every mouse move. I find this counterintuitive when the `names` are specified upon creating the HoverTool, as seen below.

For selections, one can use the ColumnDataSource like this:
```
linesource = ColumnDataSource({'x': x, 'y': x})
linesource.selected.js_on_change('indices', callback)
```
Would it be possible to add something similar:
`linesource.hovered.js_on_change('indices', callback)`


#### Complete, minimal, self-contained example code that reproduces the issue
```
from bokeh.plotting import figure, output_file, show, save
from bokeh.models import ColumnDataSource, HoverTool, CustomJS
import bokeh

# Data
x = [[0,1,2],[4,5,6]] 
y = [[0,1,0],[1,2,1]]
time = [[-3,-3.5,-5], [-1,-2,-2.5]] 
amplitude = [[-1,-2,-1], [-1,-2,1]]

p = figure(plot_width=400, plot_height=400, tools="", toolbar_location=None, title='Hover (Bokeh ' + bokeh.__version__ + ')')

# CDS
linesource = ColumnDataSource({'x': x, 'y': x})
linesource2 = ColumnDataSource({'time': time, 'amplitude': amplitude})

# Renderers
mylines = p.multi_line(xs='x', ys='y', source=linesource, name='blue', color='darkblue', hover_color='darkblue')
myotherlines = p.multi_line(xs='time', ys='amplitude', name='red', source=linesource2, color='darkred', hover_color='darkred')
mylines.hover_glyph.line_width = 3
myotherlines.hover_glyph.line_width = 3
# HoverTool
code = """
console.log('1d ind: ' + cb_data.index['1d'].indices + ', cb_data.renderer.name = ' + cb_data.renderer.name)
fig.title.text = '1d ind: ' + cb_data.index['1d'].indices + ', cb_data.renderer.name = ' + cb_data.renderer.name + ' - ' + 'Bokeh '  + ver

"""
callback = CustomJS(
    args={
        'lines': mylines.data_source,
        'otherlines': myotherlines.data_source,
        'fig': p,
        'ver': bokeh.__version__},
    code=code)
p.add_tools(HoverTool(tooltips=None, names=['blue', 'red'], callback=callback))

# Save
output_file("hover_callback.html")
save(p)
```

#### Screenshots or screencasts of the bug in action
![hover_manytrig](https://user-images.githubusercontent.com/5602677/54987648-3944dd00-4fb5-11e9-8d11-f346c8186565.gif)

user: rysport
issue_8787: state: open
title: vline hover misses some multi-lines
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
Bokeh 1.0.4, but same issue in 0.13 and 1.0.1.
Python 2.7.15
Anaconda custom (64-bit) (default, Nov 13 2018, 23:04:45) 
[GCC 7.3.0] on linux2
Tested on latest Chrome and Opera.

#### Description of expected behavior and the observed behavior
When plotting the example below, a lot of hover events are triggered as seen in the console, even though the HoverTool is specified to trigger on specified multi_lines.

Many of these events have empty `cb_data.index['1d'].indices` field, and in the case with two multi-lines, it seems that both trigger the callback as seen by `cb_data.renderer.name`.

Perhaps unrelated, but setting HoverTool `mode='vline'` results in only the blue multi_line triggering. I'm uploading a gif on this behavior below as well.


#### Complete, minimal, self-contained example code that reproduces the issue
```
from bokeh.plotting import figure, output_file, show, save
from bokeh.models import ColumnDataSource, HoverTool, CustomJS
import bokeh

# Data
x = [[0,1,2],[4,5,6]] 
y = [[0,1,0],[1,2,1]]
time = [[-3,-3.5,-5], [-1,-2,-2.5]] 
amplitude = [[-1,-2,-1], [-1,-2,1]]

p = figure(plot_width=400, plot_height=400, tools="", toolbar_location=None, title='Hover (Bokeh ' + bokeh.__version__ + ')')

# CDS
linesource = ColumnDataSource({'x': x, 'y': x})
linesource2 = ColumnDataSource({'time': time, 'amplitude': amplitude})

# Renderers
mylines = p.multi_line(xs='x', ys='y', source=linesource, name='blue', color='darkblue', hover_color='darkblue')
myotherlines = p.multi_line(xs='time', ys='amplitude', name='red', source=linesource2, color='darkred', hover_color='darkred')
mylines.hover_glyph.line_width = 3
myotherlines.hover_glyph.line_width = 3
# HoverTool
code = """
console.log('1d ind: ' + cb_data.index['1d'].indices + ', cb_data.renderer.name = ' + cb_data.renderer.name)
fig.title.text = '1d ind: ' + cb_data.index['1d'].indices + ', cb_data.renderer.name = ' + cb_data.renderer.name + ' - ' + 'Bokeh '  + ver

"""
callback = CustomJS(
    args={
        'lines': mylines.data_source,
        'otherlines': myotherlines.data_source,
        'fig': p,
        'ver': bokeh.__version__},
    code=code)
p.add_tools(HoverTool(tooltips=None, names=['blue', 'red'], callback=callback))

# Save
output_file("hover_callback.html")
save(p)
```
#### Screenshots or screencasts of the bug in action
![hover_manytrig](https://user-images.githubusercontent.com/5602677/54928568-62616100-4f14-11e9-8ec7-0d664837b25c.gif)

Here's the `mode='vline'` behavior:
![hover_vline2](https://user-images.githubusercontent.com/5602677/54929525-fc75d900-4f15-11e9-827e-04970165c16f.gif)


user: rysport
issue_8766: state: open
title: box_select not working as active_drag for gridplot
body: **ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)**

```
Python version      :  2.7.15 (default, Oct  4 2018, 22:42:40) 
IPython version     :  5.8.0
Tornado version     :  4.5.3
Bokeh version       :  1.0.4
BokehJS static path :  /usr/local/lib/python2.7/site-packages/bokeh/server/static
node.js version     :  v10.15.1
npm version         :  6.7.0
```

```
OS: Mac OS X Yosemite 10.10.5
Browser: Google Chrome 72.0.3626.121 (Official Build) (64-bit)
```

**Description of expected behavior and the observed behavior**
The box_select should be the active drag tool in the gridplot's toolbar. Now it looks like always the first drag tool is being active (PanTool in example below)

**Complete, minimal, self-contained example code that reproduces the issue**
```
from bokeh.plotting import figure, show
from bokeh.layouts import gridplot

plots = [figure(active_drag = 'box_select', tools = 'pan,wheel_zoom,box_zoom,reset,tap,crosshair,save,box_select') for i in range(6)]
show(gridplot(children = plots, ncols = 3, merge_tools = True))
```

user: tony-bony
issue_8711: state: open
title: Range-based aspect management doesn't cooperate with layout
body: Currently either layout will be wrong (cutting of axis labels) or there will be an infinite update loop between range-based aspect manager and layout. This can be seen in examples `integration/glyphs/hex_tile` and `plotting/file/aspect`. This isn't a regression, because old layout had similar issues, and the problem is with aspect management scheme.
user: mattpap
issue_8683: state: open
title: API to define custom tools that have python callbacks
body: Currently we can define a CustomAction tool to run CustomJS callbacks. It would be appropriate to easily define custom tool buttons that can invoke python callbacks as well.
user: deluksic
issue_8659: state: open
title: BokehJS standalone: hexbin example
body: Is there any example / reference to create hexbin using BokehJS JS library as same as [https://bokeh.pydata.org/en/latest/docs/gallery/hexbin.html](https://bokeh.pydata.org/en/latest/docs/gallery/hexbin.html).

I have tried to create same example using `Bokeh.HexTile`, but not able to create same look and feel as python hexbin example.
user: tavinashb
issue_8626: state: open
title: Memory leak when trying to repeatedly display plots using the same output widget
body: I'm not sure if this belongs here or at bokeh/jupyterlab_bokeh#63, but I figured I would post it here as well, as this appears to get more traffic than the jupyterlab_bokeh repository.

When I try to use bokeh in Jupyterlab to plot multiple times in a single Output widget, the old bokeh plots appear to stick around in memory even though they aren't actually accessible.

The following code will reproduce the issue (open up chrome's memory profiler and watch the memory increase as you click next).

```python
from bokeh.plotting import figure, output_notebook, show

import ipywidgets as widgets
from ipywidgets import Layout, VBox
output_notebook()
import numpy as np

def plot_sample_bokeh(i):
    f = figure(title="test")
    f.line(np.arange(100), np.random.randn(100))
    
    show(f)

to_annotate = np.arange(100)
cur_index = 0

def display_img_widget(i):
    with out_widget:
        plot_sample_bokeh(i)
        
def on_button_click(b):
    global to_annotate, cur_index
    cur_id = to_annotate[cur_index]
    
    cur_index += 1
    
    if cur_index == len(to_annotate):
        print('End of collection')
        return
    out_widget.clear_output(wait=True)
    display_img_widget(to_annotate[cur_index])

a_button = widgets.Button(description='next', button_style='primary')
a_button.on_click(on_button_click)

out_widget = widgets.Output(width=1100, height=600)

display_img_widget(to_annotate[0])

form_item_layout = Layout(
    display='flex',
    flex_flow='row',
    justify_content='flex-start', width='100%')

form = VBox([a_button, out_widget], layout=form_item_layout)

form
```

I'm fairly sure that this is a bokeh specific thing.  Replacing `plot_sample_bokeh` with an `ipywidget.Label` doesn't show the same behavior -- It will increase memory, but that memory will decrease again when the garbage collector is run.

jupyterlab: 0.35.4
jupyterlab_bokeh: v0.6.3
bokeh: 1.0.4
user: spott
issue_8609: state: open
title: When no tool `drag` is defined, `active_drag` after `add_tool` does nothing with a `PointDrawTool`
body: Below is a minimal reproducible example demonstrating this (Bokeh 1.0.2, Python 3.7)

Expected behaviour of the code below: the `PointDrawTool` is active
Actual behaviour: no tool is active

```
from bokeh.plotting import figure
from bokeh.io import output_file, show
from bokeh.models import PointDrawTool

output_file("test.html")

# no drag tools
p = figure(width=400, height=400, tools='reset')

p.circle([2, 3, 5, 6, 8, 7], [6, 4, 3, 8, 7, 5], size=30)

draw_tool = PointDrawTool()
p.add_tools(draw_tool)

# set draw to active
p.toolbar.active_drag = draw_tool

show(p)
```

If we set a valid `drag` tool on figure (replace `'reset'` by `'pan'`), `active_drag` works as expected (but `pan` is now a tool; I would like to have no tools except `PointDrawTool`)
user: jorgecarleitao
issue_8608: state: open
title: [Feature request] automatic reconnect to server
body: I've used bokeh to build a dashboard where the interaction is mostly the server pushing new information to the client. At the moment if I restart the server, the client stops updating but there is no indication that the connection is broken. It would be useful to
(a) have the client automatically detect that the connection is broken and attempt to reconnect to the server (Dash seems to do this quite well); and/or
(b) have a way to indicate to the user that the connection is broken.
user: bmerry
issue_8597: state: open
title: Ensure that replacing model reconnects signals on new model
body: In #8594 we discovered that there doesn't seem to be any mechanism for signals to be reconnected when a model is replaced on another model. For example when replacing the ``glyph`` on a ``GlyphRenderer`` or the ``tile_source`` on a ``TileRenderer``, the renderer should unsubscribe to events on the old model and re-subscribe to any change events on the model but there doesn't seem to be such a mechanism at the moment (unless @bryevdv and I missed something).

It would be nice if a property could handle this automatically by discovering the signals connected to the old model and then setting up those same signals for the model its being replaced with but I know nothing about the feasibility of such a mechanism.
user: philippjfr
issue_8521: state: open
title: Feature request: BoxEdit drag edge of boxes and restrict to one dimension
body: Bokeh version: 1.0.2

From the documentation, BoxEditTool is described to add, move and delete boxes.

I have two feature requests for this tool:

* I want to be able to change the width or height of a box by dragging on an edge of the box.
* I want to be able to specify a dimension in which a box can be defined with other dimension fixed. Eg, draw a box with y edges fixed to min, max of a plot and x edges are freely adjusted.
user: DancingQuanta
issue_8512: state: open
title: Info box for supplementary usage information
body: One general problem when building custom bokeh tools (e.g poly edit tools and similar) is that it can be hard to discover the set of modifier keys used by the tool. One way round this is to explain how to use the tool in text (e.g in the markdown of a notebook) or to add `Div` models with some explanation. Another approach that might be worth considering could be a dynamic information box. For example, here is how Inkscape does it:

![image](https://user-images.githubusercontent.com/890576/50164106-72c7c700-02a7-11e9-9622-c5874250c58b.png)

This example lets you see the effect of holding `Ctrl` or `Enter` while using this path creation tool.

As a wishlist item, I would like to see something similar in Bokeh. Perhaps this could be an area that is placed next to the toolbar (when the toolbar is horizontal) or maybe you could have a small horizontal area to show text at the bottom of plots. Such an area could have other uses other than just describing tools e.g you might want to show the x/y position of the cursor in the viewport.


user: jlstevens
issue_8503: state: open
title: Cannot combine multi-line and (multi-marker) scatter legends (feature request)
body: Thanks to the new features available in Bokeh >= 1.0.0, I was hoping to make progress towards improving the performance issues described in #7066 for plots with multiple lines and multiple markers (and multiple line dashes, but that development has been unfortunately stopped #2960). I get a 4x-8x performance improvement factor (very nice guys!), but I don't think multiple line and multiple marker legends can currently be combined. We can either display line legends or marker legends, but not a combination of both. Also, the data must be provided in two different data sources because of differences in the expected data format.

Feature request:
* Provide a way to combine multi-line and multi-marker scatter legends
* Standardize the interface to `multi_line` by following the principles of the scatter one. That would directly provide the feature above, but break backward compatibility... An alternative would be to generalize the `Line` glyph to mimic the behaviour of `scatter`, so that when a field is given for the `color` argument, a group-by is performed and each group is plotted as an independent line (as long as it maintains the performances of `multi_line` of course)
* While we are at it, allowing `line_dash` to be a field would be nice as well...

My test case (expanding #7066, a figure containing multiple straight lines with various slopes):
```
import numpy as np
import itertools
from bokeh.palettes import plasma
from bokeh.plotting import figure, show, ColumnDataSource
from bokeh.models import Legend, LegendItem
from bokeh.transform import factor_cmap, factor_mark

##########
# Inits

x = np.linspace( 0, 10, 50 )
a = list( range( 20 ) )

markers = itertools.cycle( 'asterisk circle diamond cross square triangle inverted_triangle'.split() )
markers = [ next( markers ) for ii in range( len( a ) ) ]

colors = plasma( len( a ) )

##########
# Bokeh: full case, using new .scatter() with varying marker (v >= 1.0.0)

# create data sources
# Must be done twice, since the interface to multi_line is different from the
# one to scatter (list of lists vs flat list and grouping field)
legs = [ str( this_a ) for this_a in a ]
y_lines = [ this_a * x for this_a in a ]
linedatasource = ColumnDataSource( { 
        'x': [ x ] * len( a ), 
        'y': y_lines,
        'colors': colors,
        'legs': legs } )
markerdatasource = ColumnDataSource( {
        'x': np.tile( x, len( a ) ), 
        'y': np.array( y_lines ).flatten(),
        'legs': np.repeat( legs, len( x ) ) } )

p = figure(tools='')
m = p.scatter( 'x', 'y', source=markerdatasource, 
               marker=factor_mark( 'legs', markers, legs ), # could also fill a 'marker' column instead
               color=factor_cmap( 'legs', colors, legs ) ) # could also fill a 'color' column instead
l = p.multi_line( 'x', 'y', source=linedatasource, color='colors' )

legend = Legend( items=[ LegendItem( label=leg, renderers=[m,l], index=ii ) for ii,leg in enumerate( legs ) ], 
                 location=(10, 0) ) # --> index is wrong for `m` (on all data instead of groups probably)
#legend = Legend( items=[ LegendItem( label=leg, renderers=[l], index=ii ) for ii,leg in enumerate( legs ) ]
#                       + [ LegendItem( label=field('legs'), renderers=[m] ) ] , # use field to force field name recognition. In that case the legends are doubled
#                 location=(10, 0) )
p.add_layout( legend, 'right' ) 

# -> 4x->8x faster than bokeh with explicit loop on `a`, but:
# Must choose between legends for lines or markers (can't have both)
# No multiple line dashes
    
show(p)        
```

I use Bokeh 1.0.2

user: jxrossel
issue_8501: state: open
title: When updating legend, location update doesn't work well
body: Hi there, 

Using your exemple from http://bokeh.pydata.org/en/latest/docs/user_guide/styling.html#outside-the-plot-area (that work well ) i tried to make an update about legend. And it didn't work... The x position is not taken into account but the y position is. 
Here is a verry simplified  example:

```
import numpy as np
from bokeh.models import Legend
from bokeh.plotting import figure, show, output_file
from bokeh.models.widgets import Button
from bokeh.io import curdoc
from bokeh.layouts import column

x = np.linspace(0, 4*np.pi, 100)
y = np.sin(x)


p = figure(toolbar_location="above")

r0 = p.circle(x, y)
r1 = p.line(x, y)

r2 = p.line(x, 2*y, line_dash=[4, 4], line_color="orange", line_width=2)

r3 = p.square(x, 3*y, fill_color=None, line_color="green")
r4 = p.line(x, 3*y, line_color="green")

legend = Legend(items=[
    ("sin(x)"   , [r0, r1]),
    ("2*sin(x)" , [r2]),
    ("3*sin(x)" , [r3, r4]),
], location=(0, -30))

p.add_layout(legend, 'right')

def update_graph():
    for el in p.renderers:
                if isinstance(el, Legend):
                    p.renderers.remove(el)
    legend_new = Legend(items=[
            ("sin(x)"   , [r0, r1]),
            ("2*sin(x)" , [r2]),
            ("3*sin(x)" , [r3, r4]),
            ], location=(0, -30))
    
    p.add_layout(legend_new, 'right')


update = Button(label="update")
update.on_click(update_graph)

curdoc().add_root(column(p, update))

```

Before: 
![image](https://user-images.githubusercontent.com/44472836/49949769-beb8eb80-fef6-11e8-8560-5c5dad747d67.png)

After: 
![image](https://user-images.githubusercontent.com/44472836/49949792-ce383480-fef6-11e8-95bd-4c88239cc81d.png)

Versions:
bokeh==1.0.0
Python 3.7.0
OS: Windows

Thank you in advance and have a nice day. 
user: pauostr
issue_8499: state: open
title: Bring back the functionality needed to embed separate Bokeh roots within React
body: BokehJS 0.12.16 has [`embed/add_model_from_session`](https://github.com/bokeh/bokeh/blob/0.12.16/bokehjs/src/lib/embed.ts#L210) function that can be used to embed an arbitrary root anywhere. In particular, I use it to embed separate components of the same document in different parts of a React application.

Commit 3eaa05a6fcef60abc7680d8b4465da0032750a35 removed this function, and I don't think that right now there's a way to embed separate roots without embedding the whole document at once.

Note that this comment may also be relevant: https://github.com/bokeh/bokeh/pull/7708#discussion_r235605875
user: p-himik
issue_8498: state: open
title: Proposed update table column widths when column width resized
body: Hi,

I noticed that if I do this in js:

```
table.column[i].width=100
table.change.emit()
```

then the table columns are resized as expected.  However, if I click and drag on the column widths and then try to inspect the values of the table columns widths, they are unchanged.  

My proposal is to update data_table.ts and subscribe to SlickGrid onColumnsResized.  The use case for me is that I have a page loaded, decide to change some column widths with my mouse, then make some other updates in the page which trigger a callback that ultimately pings a server and updates the underlying cds.  The table is then re-rendered and the column widths revert to their previous state.  I tried to fix this by inspecting the column widths of the table before issuing the ajax request so that I could then reset the table column widths to their state after I resized with the mouse.  However, I noticed that the tableColumn.width value is not affected by the column resize event.

Long story short, my proposal in data_table.ts::DataTableView::render is:

```
  this.grid.onColumnsResized.subscribe((_event: any, args: any) => {                                                                                   
            columns=args.grid.getColumns()                                                                                                                   
            for (var i=1; i!=columns.length; ++i){                                                                                                           
                this.model.columns[i-1].width=columns[i].width || this.model.columns[i-1].width                                                              
            }                                                                                                                                                
        })              
```

Before posting the pull request, though that I'd create the issue and get feedback in case there is already a solution that I've missed.

Also, I am welcome to any suggestions on improving the code quality.

Thanks,
Justin
user: jgriffi6
issue_8487: state: open
title: Wheel zoom doesn't work with match_aspect=True and given range bounds
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
bokeh: 1.0.1 (installed via pip)
python: Python 3.7.0 (default, Jun 28 2018, 13:15:42), via miniconda
OS: Ubuntu 18.04.1 LTS
Browser: Firefox 63.0.3 (64 bit)

#### Description of expected behavior and the observed behavior

In a plot with `match_aspect=True` and given data range bounds, **I cannot use the wheel zoom tool initially**. After a box zoom, using wheel zoom  is possible, **but only within the bounds of
the initial box zoom**.

#### Complete, minimal, self-contained example code that reproduces the issue

```
from bokeh.models.ranges import DataRange1d
from bokeh.plotting import figure, show

sx = 10
sy = 5
x_range = DataRange1d(bounds=(0,sx))
y_range = DataRange1d(bounds=(0,sy))
plot = figure(x_range=x_range, y_range=y_range, match_aspect=True)
plot.rect(x=[0,sx,sx,0,sx/2], y=[0,0,sy,sy,sy/2],
          height=1, width=1) # plots a square at each corner and in the middle
show(plot)
```
Now I can select wheel zoom, but nothing happens. If I use the box zoom and zoom in,
then using the wheel zoom is possible, but I cannot fully zoom out.

Same applies if I use `bounds='auto'` instead.

#### Stack traceback and/or browser JavaScript console output

No traceback, Javascript output:
```
[bokeh] setting log level to: 'info' bokeh-1.0.1.min.js:31:38198
[bokeh] document idle at 104 ms bokeh-1.0.1.min.js:31:101288
Source-Map-Fehler: request failed with status 404
Ressourcen-Adresse: https://cdn.pydata.org/bokeh/release/bokeh-1.0.1.min.js
Source-Map-Adresse: bokeh.min.js.map[Weitere Informationen]
```
(I don't think this output is relevant)



user: mcrot
issue_8437: state: open
title: BokehJS standalone: chart interaction example
body: I am trying to implement interactive chart using BokehJS standalone JS version 1.0.0

I am facing following issues - 

- Not able to find any example or official documentation regarding the same

- How to bind JavaScript function directly instead  of using
 `Bokeh.CustomJS({args:{"plot":plot},code: ""})`






user: tavinashb
issue_8436: state: open
title: Alpha value of patches not working properly for MultiPolygons (GeoJSONDataSource)
body: When drawing multipolygons via a GeoJSONDataSource and when using an alpha_value < 1, there is some strange behaviour when zooming or dragging. As you can see, the alpha_value is set to 1 when zooming out at some level and then back to its normal value when zooming in again. This can be seen in the animation below

![bokeh_polygon_multipolygon_alpha_issue](https://user-images.githubusercontent.com/31643597/48542544-bc477f80-e8bf-11e8-9243-a14d1095da2c.gif)

The following code can be used to reproduce the issue:

```python
from bokeh.plotting import output_file, show, figure
from bokeh.models import GeoJSONDataSource
from bokeh.tile_providers import CARTODBPOSITRON_RETINA
from bokeh.layouts import row
output_file("Bokeh_MultiPolygon_alpha_issue.html")

#Create Plot for Polygon (alpha works fine):
geojson = '{"type": "FeatureCollection", "features": [{"id": "0", "type": "Feature", "properties": {}, "geometry": {"type": "Polygon", "coordinates": [[[1047812.9756104371, 7327520.138088438], [1048956.2201364043, 7327632.6102160765], [1049917.1363028432, 7325408.423646096], [1050584.1727529466, 7324868.073887764], [1049577.0963943023, 7323163.544035019], [1050488.3246827312, 7320973.128262942], [1050671.2947552782, 7323726.571402881], [1051080.5757399767, 7324029.650893333], [1050905.052470055, 7323253.039123812], [1051276.6823115353, 7324138.87245766], [1052273.0812145576, 7324604.437060457], [1053419.9557568927, 7327276.5278666755], [1056513.1130692943, 7327706.892693922], [1056650.3847130411, 7326014.784424612], [1058139.7477229517, 7325118.043726692], [1057961.7871530307, 7323952.538187691], [1057501.4934178593, 7323845.74713336], [1058137.9996373702, 7323069.28734159], [1057421.1434445973, 7322886.2328310525], [1058266.4706403427, 7321620.5836589625], [1057777.8723768487, 7319145.144975949], [1057913.3162105978, 7317948.365186131], [1058182.805275363, 7317955.485729853], [1056793.206426833, 7317210.865421337], [1054719.8973324778, 7317814.295262741], [1054445.9737735558, 7316762.689237871], [1053112.8550985735, 7315481.557520173], [1053189.834300677, 7314310.51888764], [1052312.914150965, 7313905.070567016], [1044067.1202709799, 7314113.88841872], [1043278.2730269807, 7315206.389628218], [1041681.8784029831, 7319449.092037197], [1047027.3425207186, 7322288.9501458565], [1046655.2011012785, 7324753.18571598], [1047684.8434857774, 7326238.624537074], [1046925.8577715933, 7327489.600824937], [1047812.9756104371, 7327520.138088438]]]}}]}'
geosource = GeoJSONDataSource(geojson=geojson)
p_works = figure(
    plot_width=500,
    plot_height=500,
    x_axis_type="mercator",
    y_axis_type="mercator",
    active_scroll="wheel_zoom",
    title="Polygon with alpha works just fine")
p_works.add_tile(CARTODBPOSITRON_RETINA)
p_works.patches(xs="xs", ys="ys", source=geosource, alpha=0.5)

#Create Plot for MultiPolygon (alpha has strange behaviour):
geojson = '{"type": "FeatureCollection", "features": [{"id": "55", "type": "Feature", "properties": {}, "geometry": {"type": "MultiPolygon", "coordinates": [[[[818595.6013313349, 7052762.380699109], [818439.9848981319, 7051504.062840309], [819125.4296329201, 7051635.598220743], [819266.928480915, 7050602.642542507], [818324.2541759631, 7050273.612711799], [818219.7162885794, 7048787.510823077], [819728.4380110737, 7049635.735427077], [820540.330412476, 7049437.204835317], [820422.7448821585, 7048304.748887886], [820960.8668254825, 7047691.9142534295], [822683.1135994653, 7048518.3789069075], [824523.1442177302, 7048381.488084926], [825068.0122622185, 7047720.028060778], [826146.1583447303, 7049097.141495499], [827252.0333149377, 7049459.850696863], [827298.0256290742, 7050308.006972921], [827801.2308382791, 7050734.848020952], [827947.3487248071, 7050199.288414724], [828721.7786604336, 7050621.745307863], [829154.0725997243, 7050142.855434429], [830454.0645933991, 7051589.463657213], [833396.0567501415, 7051809.100913065], [836005.7982995465, 7049810.821212676], [835934.1676175263, 7049390.140000168], [836696.7495441957, 7048579.047040368], [838176.1986260047, 7048483.079127526], [838388.0520300153, 7049134.206829761], [840082.2652362472, 7049288.193259389], [841837.5721029943, 7047156.254202486], [844571.4612297274, 7046396.5954115875], [845712.221912003, 7046907.713159114], [845803.9449682644, 7046152.585285757], [845428.0139833352, 7045920.28688502], [845697.3745544358, 7044699.008445359], [846297.446580148, 7045167.977893598], [848941.3736594184, 7045108.497854298], [849371.3733757236, 7046221.995410942], [848047.1482017288, 7046742.463191], [848176.8060841929, 7047275.60871024], [850105.6947125525, 7046396.603657157], [851889.4772619429, 7047434.273308887], [853063.1491847129, 7046144.320389393], [860466.1807418028, 7049642.65798674], [870015.7586249615, 7052751.227828378], [871496.668945638, 7051984.754252501], [872069.354042408, 7050406.663377055], [876253.941389854, 7052829.568958384], [877821.8216108405, 7048369.442206723], [873873.7274972242, 7037567.795047719], [863934.8206189744, 7027779.385162705], [861063.7733768952, 7026900.747692722], [860564.890641347, 7026007.839887588], [857395.9445917461, 7024324.045464247], [856886.5795394611, 7022853.737759841], [857355.6784713768, 7022566.067872766], [857517.5652624543, 7021648.197901242], [857072.8899226994, 7021631.2228728365], [859277.7211064356, 7016156.294120939], [856764.4590122114, 7016501.67170882], [856310.440575467, 7015906.200266359], [856093.5254753085, 7016524.443244503], [855069.3709567924, 7016717.38606763], [853438.1089562009, 7015964.445521176], [851722.4619062053, 7017358.34923665], [851346.6364708415, 7016860.810734823], [850764.1453875208, 7010263.789072959], [851153.1156628674, 7010111.977283982], [850526.7372830705, 7009471.506591512], [850714.55218815, 7007292.093718875], [850173.0140433875, 7007302.6776842605], [850143.0549577723, 7004132.591762858], [849048.8626344493, 7004193.230342204], [847350.6006103272, 6993679.362584731], [846929.0417462823, 6993965.406505144], [842120.7607755585, 6990563.834984132], [841757.4264497056, 6990174.499807995], [841974.7315188058, 6989766.803639373], [839919.3704838476, 6989229.531345423], [837633.9209916781, 6991022.63049964], [836932.779380992, 6993688.277737141], [833021.3347022173, 6998964.185341003], [824584.2556303628, 6999713.242030311], [824080.9380097073, 6999932.148988248], [824718.8719843394, 7001381.809774608], [822118.7651481355, 7002622.988198126], [821720.9801816327, 7000579.298749789], [819625.3119590331, 7002596.468207994], [816412.7191322615, 7001866.3814062895], [815890.8332539076, 7002469.435361307], [816383.9012414748, 7003349.996667424], [816099.3638744693, 7003734.821905079], [814486.875385483, 7003219.50411592], [811329.9793717363, 7004353.260660866], [809652.3467695639, 7004023.278323603], [806533.741406858, 7004769.196621913], [801811.400377572, 7003979.510546732], [799592.9154664858, 7005542.605231254], [799911.4243128092, 7006003.064925488], [799376.2978584499, 7006654.842460346], [799155.7185840119, 7008670.950555849], [800346.1365686455, 7010425.874967999], [800438.7704525508, 7012975.701445002], [801916.9636894616, 7015712.541339935], [804511.0079135533, 7016348.335195478], [802386.3897208237, 7017938.815803754], [803459.9701146826, 7019887.314400226], [803384.597323006, 7022767.014197598], [802668.8870428182, 7025358.516864532], [802902.5376977723, 7025318.090619515], [802494.9977912948, 7028085.480810289], [803713.1621674564, 7029187.864767282], [805153.9073892734, 7032643.265904833], [805592.6030226203, 7036339.222234631], [804731.6254730888, 7037583.697207034], [806236.8561505303, 7039187.252171191], [806574.907242316, 7042283.128838557], [807445.8190759175, 7042824.717156853], [812031.7631021998, 7041908.303373751], [812921.7621520872, 7043365.519703051], [813170.2680209877, 7048004.131777806], [814915.3124715878, 7047983.860781618], [814909.7558586155, 7049896.306873291], [815438.793707049, 7049506.460191388], [815970.1973023182, 7050032.843997977], [815908.1214366652, 7051903.029835911], [816437.4961035629, 7051974.581596053], [816271.7630828072, 7050066.015013026], [817101.3204448564, 7050239.412449229], [817125.3293900649, 7049250.462679739], [817916.9355501583, 7049605.016637376], [818595.6013313349, 7052762.380699109]]], [[[757010.0216354087, 7096164.565692758], [758443.9573807763, 7095200.738415555], [756625.9154900918, 7094233.875749463], [751921.9160683481, 7094057.361379415], [749800.1605676216, 7092341.360247497], [747799.2173584495, 7091671.396036088], [750134.3879609886, 7090157.9225468375], [750445.1225676733, 7089296.882088086], [752259.3829528907, 7088690.194720548], [752370.6922055054, 7087819.618373694], [751659.6066075225, 7087091.679053541], [751737.4382346631, 7087808.105212739], [751088.9228710252, 7087758.060663443], [751037.184981905, 7087188.822763083], [751373.943229763, 7087020.840737467], [751115.4860833724, 7086774.660957445], [750514.6028852911, 7087289.124248463], [750345.3587104055, 7086723.960583601], [750419.6436229941, 7087361.289343585], [750771.7714609953, 7087700.580566008], [750320.9949008325, 7088663.103698078], [749445.4006930303, 7089169.652198432], [746561.9170757964, 7088804.546300089], [747939.8819636595, 7086965.541665389], [741981.6720986357, 7090546.962387155], [741050.9265781497, 7091695.313617933], [740697.1459266096, 7093864.4930207515], [742169.4773201004, 7095361.736767016], [751333.5637291251, 7097998.526567789], [753858.8706589934, 7097769.213430428], [757010.0216354087, 7096164.565692758]]], [[[764209.5521985273, 7093093.062231672], [764268.6066265288, 7093061.413376727], [764644.9287759027, 7093241.427388862], [763916.6609428272, 7092853.781194893], [764209.5521985273, 7093093.062231672]]]]}}]}'
geosource = GeoJSONDataSource(geojson=geojson)
p_notworking = figure(
    plot_width=500,
    plot_height=500,
    x_axis_type="mercator",
    y_axis_type="mercator",
    active_scroll="wheel_zoom",
    title="Multipolygon with strange alpha behaviour")
p_notworking.add_tile(CARTODBPOSITRON_RETINA)
p_notworking.patches(xs="xs", ys="ys", source=geosource, alpha=0.5)

layout = row(p_works, p_notworking)
show(layout)
```


The output HTML is also stored here:
[Bokeh_MultiPolygon_alpha_issue.zip](https://github.com/bokeh/bokeh/files/2584519/Bokeh_MultiPolygon_alpha_issue.zip)



user: PatrikHlobil
issue_8421: state: open
title: Can't change type of figure.x_range dynamically
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)

```
$ bokeh info
Python version      :  3.7.0 (v3.7.0:1bf9cc5093, Jun 26 2018, 23:26:24)
IPython version     :  7.1.1
Tornado version     :  5.1.1
Bokeh version       :  1.0.1
BokehJS static path :  /path/to/my/python3.7/site-packages/bokeh/server/static
node.js version     :  v9.7.1
npm version         :  5.6.0
```

#### Description of expected behavior and the observed behavior

I am trying to implement a plot where the user can select a column from a dropdown and see the histogram of values in the column. Some columns may be categorical, and some are continuous, so I've using a `vbar` and setting the width accordingly. However, this means that I sometimes need a `DataRange1d`, and sometimes a `FactorRange`. Changing my figure's `x_range` to a `FactorRange` causes my plot to be reset and not show any output. There is no error, the glyphs just disappear. 

#### Complete, minimal, self-contained example code that reproduces the issue

This is about as minimal as I could get it, sorry.

```python
from bokeh.plotting import Figure, ColumnDataSource
from bokeh.models.widgets import Dropdown
from bokeh.models.ranges import DataRange1d, FactorRange
from bokeh.layouts import row
from bokeh.io import curdoc

import pandas as pd
import numpy as np


def _col_stats(col,):
    """Value counts or histogram of column"""
    is_str = isinstance(col.values[0], str)

    if is_str:
        counts = col.value_counts().sort_index()
        hist = counts.values
        edges = np.array(counts.index)

    else:
        hist, edges = np.histogram(col, bins="auto")

    return hist, edges


def _hist_source_dict(hist, edges):
    if isinstance(edges[0], str):
        return {"top": hist, "x": edges, "width": 0.8 * np.ones_like(edges)}

    else:
        w = np.diff(edges)
        x = np.array(edges[:-1]) + w / 2
        return {"top": hist, "x": x, "width": w}


def _update_histogram(figure, source, hist, edges, name=""):
    """Why doesn't this work?"""
    if isinstance(edges[0], str):
        figure.x_range = FactorRange(factors=edges)
    else:
        figure.x_range = DataRange1d()

    source.data = _hist_source_dict(hist, edges)

    if name:
        figure.title.text = name


df = pd.DataFrame(
    {"a": [1, 2, 3, 4, 3, 1, 2, 3, 4, 1], "b": list("abcdcbaeda")}
)
columns = df.columns

# initial values
first_col = columns[0]
first_hist, first_edges = _col_stats(df[first_col])

# set up data sources
hist_source = ColumnDataSource(data=_hist_source_dict(first_hist, first_edges))

# set up dashboard components
plot = Figure(
    title=first_col,
    tools="xpan,xwheel_zoom,box_zoom,reset",
    active_drag="xpan",
    active_scroll="xwheel_zoom",
)

plot.vbar(top="top", x="x", width="width", bottom=0, source=hist_source)

dropdown = Dropdown(
    label="Column", menu=[(col, col) for col in df.columns], value=first_col
)

# configure callbacks
def dropdown_callback(attr, old, new):
    global df, plot, hist_source
    hist, edges = _col_stats(df[new])
    _update_histogram(plot, hist_source, hist, edges, new)


dropdown.on_change("value", dropdown_callback)

# Position elements in document
root = row(plot, dropdown)
curdoc().add_root(root)
```

If you save this and run it with `bokeh serve`, selecting 'b' from the dropdown will blank out the plot, but there is no error.

#### Screenshots or screencasts of the bug in action
Continuous column:
![image](https://user-images.githubusercontent.com/18459252/48340325-a55c1f80-e66a-11e8-95b2-58c048c2edf2.png)

Categorical column:
![image](https://user-images.githubusercontent.com/18459252/48340337-adb45a80-e66a-11e8-978c-c7aa62a48c00.png)


user: JohnPaton
issue_8408: state: open
title: CategoricalTicker could accept offset argument
body: This is a feature request.

#### Description of expected behavior and the observed behavior
The current implementation of CategoricalTicker places "band" boundaries in the middle of the category markers. If the current CategoricalTicker would accept an offset (in category units) it could easily handle the use case illustrated below. With this feature, category-aligned glyphs would appear in "lanes" defined by the "bands". A use case is illustrated below.

#### Example of work around that produces desired behavior.
(pulled from a Stackoverflow post)
```
CS_CODE = """
import {CategoricalTicker} from "models/tickers/categorical_ticker"

export class BetweenCategoryTicker extends CategoricalTicker
  type: "BetweenCategoryTicker"

  get_ticks: (start, end, range, cross_loc) ->
    ticks = super(start, end, range, cross_loc)

    # shift the default tick locations by half a categorical bin width
    ticks.major = ([x, 0.5] for x in ticks.major)

    return ticks

"""

class BetweenCategoryTicker(CategoricalTicker):
    __implementation__ = CS_CODE
```
Illustration of the current and desired output:

#### Present behavior with `CategoricalTicker()`
![undesirable results with CategoricalTicker](https://user-images.githubusercontent.com/1383080/48164697-420c6f00-e2b1-11e8-954a-b44b4b4ce9f6.png)

#### Desired behavior with `CategoricalTicker(offset=.5)`
![Hypothetical output with CategoricalTicker(offset=.5)](https://user-images.githubusercontent.com/1383080/48164698-420c6f00-e2b1-11e8-90ac-7bdf08fdce2f.png)

user: chrisjcameron
issue_8359: state: open
title: Ideas for geojson / GeoJsonDataSource / multipolygons integrations
body: Since `MultiPolygons` has landed and is closely modeled on geojson, we can do a better job with `GeoJsonDataSource` in the case of MultiPolygons.

## Currently

`GeoJsonDataSource` converts  any `MultiPolygon` to a `NaN` separated list of x and y coordinates for the exterior ring. This maps nicely to `Patches` but any holes are ignored. 

## Proposal

`GeoJsonDataSource` should leave `MultiPolygons` in their nested structure for use with the `MultiPolygons` glyph. I guess `zs` can be one degree less nested because holes have no depth.

A new test could look like:

```coffee
  describe "xy MultiPolygon", ->
    # This has two polygons, and the second one has has a hole in it
    geojson = """{
      "type": "GeometryCollection",
      "geometries": [{
        "type": "MultiPolygon",
        "coordinates": [
          [[ [102.0, 2.0], [103.0, 2.1] ]],
          [[ [100.0, 0.0], [101.0, 0.1] ],
           [ [100.2, 0.2], [100.8, 0.3] ]]
        ]
        }
      ]
    }"""
    geo = new GeoJSONDataSource({geojson: geojson})

    it "should add 3 times nested lists of xs, ys and 2 times nested lists of zs", ->
      expected_data = {'x': [NaN], 'y': [NaN], 'z': [NaN], 'xs':[[[[102.0, 103.0]], [[100.0, 101.0], [100.2, 100.8]]]], 'ys':[[[[2.0, 2.1]], [[0, 0.1], [0.2, 0.3]]]], 'zs': [[[NaN, NaN], [NaN, NaN]]]}
      expect(geo.data).to.be.deep.equal(expected_data)

```

user: jsignell
issue_8347: state: open
title: GraphRenderer breaks custom Hover callbacks
body: Hi, using CustomJS callbacks in the HoverTool appears to be broken with the GraphRenderer. The callback is never executed and `TypeError: undefined is not an object (evaluating 'n.data_source.inspected')` is printed to the browser's console. I didn't see this issue with other renderers.

I'm using  bokeh 0.13.0 and networkx 2.1 with Python 3.6.6 (installed by Anaconda) and Jupyter 4.4.0 on macOS 10.13.5 in Safari 11.1.1

#### Minimal example
```Python
from bokeh.io import show, output_notebook
from bokeh.models import Range1d, Plot, CustomJS, Circle, HoverTool
from bokeh.models.graphs import from_networkx
import networkx as nx

output_notebook()

# create the graph renderer
G = nx.Graph()
G.add_weighted_edges_from([(1, 2, 0.125), (1, 3, 0.75), (2, 4, 1.2), (3, 4, 0.375)])
graph = from_networkx(G, nx.layout.circular_layout, scale=1.8, center=(0,0))

# create a circle just to also test another renderer
c = Circle()
c.x = 0
c.y = 0
c.radius = .1

# build plot
plot  = Plot(x_range=Range1d(-3, 3), y_range=Range1d(-3, 3))
plot.renderers.append(graph)
plot.add_glyph(c)

# this just prints an error to the console: 
# "TypeError: undefined is not an object (evaluating 'n.data_source.inspected')"
hover_tool = HoverTool(
    tooltips="index: @index", 
    callback=CustomJS(code='console.log("foo");'), 
    renderers=[plot.renderers[0]]
)

# this prints "foo" and no error to the console, 
# but it also doesn't allow us to interact with the graph
#hover_tool = HoverTool(
#    tooltips="index: @index", 
#    callback=CustomJS(code='console.log("foo");'), 
#    renderers=[plot.renderers[1]]
#)

plot.add_tools(hover_tool)

show(plot)
```

user: felixpatzelt
issue_8289: state: open
title: Permanent labels on Networkx graph
body: Following up on https://stackoverflow.com/questions/49848966/how-to-add-permanent-name-labels-not-interactive-ones-on-nodes-for-a-networkx since the problem hasn't been addressed yet and I couldn't find an existing issue that covered it. 

It is really painful to add labels to networkx graphs currently, would be awesome if this could be improved.
user: codercahol
labels: grant: SDG 2023: Funded from NumFOCUS Small Development Grant 2023
issue_8288: state: open
title: Update existing graphs from new networkx data
body: Once added to a plot, it is not trivial to update the data for an existing graph renderer (the `from_networkx` function always returns a new entire `GraphRenderer`) It would be helpful to have a simple mechanism to update an existing graph renderer from new networkx data, without having to generate a new plot, or add/remove graph renderers. 

user: samirak93
issue_8277: state: open
title: bokehjs - glyph renderer sticks to the length of the dataset with which it was initialized
body: ### Software version info

Version: **bokehjs** 0.13.0 (used standalone, without its python counterpart)
Browsers: Firefox, Chromium at least

### Observed behaviour

Once initialized with a dataset (ColumnDataSource) of a certain length, a glyph renderer (I tried line and circle) sticks to that length as a ceiling for subsequent updates to the datasource. For examples,

1. If initialized with dataset of length 0, `{x: [], y: []}`, the glyph renderer never plots a glyph no matter how many times you update the underlying datasource.

2. If initialized with dataset of length 5, the renderer will always plot only the first 5 datapoints.

### Expected Behaviour

The initial dataset length should not matter to any subsequent updates. The renderer should plot whatever length of dataset updates are passed to it, provided that the basic assumption of equal column length is honoured.

### Example Code

This issue is best demonstrated in this jsfiddle: http://jsfiddle.net/bLgj4vc7/3/

More details on the related stackoverflow question and discovery of this bug posted as an answer: https://stackoverflow.com/questions/52463345/renderer-not-updating-glyph-on-plot-in-bokehjs/52503686

### Current workaround

initialize the renderer with a dataset filled with NaNs, which has a length equal or more than the maximum number of datapoints you want to plot for a single dataseries.

So, if in a line glyph, I wish to plot maximum 100 datapoints, then:

````
const plot = Bokeh.Plotting.figure()

const maxDataPoints = 100 // maximum number of points I want for this glyph
const emptyData = Array().fill().map(_ => NaN)
const cds = new Bokeh.ColumnDataSource({x: emptyData, y: emptyData})  
const renderer = plot.line({field: 'x'}, {field: 'y'}, {source: cds, line_width: 2})

// now any subsequent updates to datasource of renderer will update its glyph on the plot.
renderer.data_source.data = {
  x: dataLengthLessOrEqualToMaxDataPoints.x,
  y: dataLengthLessOrEqualToMaxDataPoints.y
}
````
user: jyssh
issue_8264: state: open
title: Widget TextInput automatically losing focus on Android 
body: 

#### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
Bokeh 13.0 server running on an AWS EC2, with python 2.7.14, trying to open on an Android 5.1.1 Tablet
 Same bug with Chrome, Edge and Firefox on the android tablet. 

Works on iPhone, Mac and Windows with chrome and IE.

#### Description of expected behavior and the observed behavior

When using the TextInput widget, when you touch the widget to write some text, _the keyboard appears than instantly disappears_, making it impossible to type something in. 

Expected behavior : to be able to type something into the input text bar and retain it 

#### Complete, minimal, self-contained example code that reproduces the issue

```
from bokeh.io import curdoc
from bokeh.models import TextInput

text_input = TextInput()
curdoc().add_root(text_input)

```


user: lblanche
issue_8250: state: open
title: test__lifecycle_hooks failed on Windows
body: I open this issue to keep a trace on this test
cf [Appveyor tests results ](https://ci.appveyor.com/project/xavArtley/bokeh/build/1.0.67/tests)
user: xavArtley
issue_8244: state: open
title: changes to glyphs added after push_notebook not synced
body: ref: #7590 

```
from bokeh.plotting import figure, output_notebook, show
from bokeh.io import  push_notebook

# output to static HTML file
output_notebook()

#### CELL

p = figure(plot_width=400, plot_height=400)

# add a circle renderer with a size, color, and alpha
r1 = p.circle([1, 2, 3, 4, 5], [6, 7, 2, 4, 5], size=20, color="navy", alpha=0.5)

handle = show(p, notebook_handle=True)

# this shows up, even after show/push_notebook
r2 = p.circle([6, 7, 2, 4, 5], [1, 2, 3, 4, 5], size=20, color="red", alpha=0.5)
push_notebook(handle=handle)

#### CELL

# this shows up, even created in another cell
r3 = p.circle([6, 7, 2, 4, 5], [4, 3, 3, 4, 5], size=22, color="green", alpha=0.5)

#### CELL

# changes below to r2, r3 have no effect, but r1 does
r3.glyph.fill_alpha = 0
push_notebook(handle=handle)
```

<img width="686" alt="screen shot 2018-09-13 at 10 03 43" src="https://user-images.githubusercontent.com/1078448/45503666-56fad500-b73c-11e8-9380-81bf0babba9a.png">

user: bryevdv
issue_8233: state: open
title: Errors originating from models/properties should be more informative
body: Errors like `DeserializationError('Int expected Integral, got 0')` (mind the zero, which is an effect of badly formatted `str`), especially in a server setup, doesn't give a clue where an issue originates from. At minimum the error message should contain object's name and id, and property's name. However, in large object graphs, a much better solution would be to provide a full path from root the the point of failure, e.g. for `Row(children=[Column(children=[Plot()], rows="bad value")])` this would be:
```py
DeserializationError("Row(id=...).children[0].rows of type Int expected Integral, got 'bad value' of type str")
```
or something along this.
user: mattpap
issue_8189: state: open
title: Feature request: Line/VectorEditTool
body: Hi,

I [posted](https://groups.google.com/a/continuum.io/forum/#!topic/bokeh/XhBaQyuSkHQ) in the Google discussion board regarding drawing lines on plots, and it appears like this is still not an existing EditTool for figures. To clarify, I am referring to a line that starts where you tap first and ends where you make the following tap (creating a straight line between the points), and of course allows changing size and location of the edges interactively. Also, selection between multipe lines (as it is in the BoxEditTool). The associated data source should probably have either two keys (for x and y lists) or one (for a list of (x, y) tuples). 

Unfortunately my JS is terrible, but I would imagine this would be relatively trivial to implement considering the complexity of the wonderful set of tools Bokeh already offers. If that's correct, I strongly believe it would be a useful addition.

Thank you very much for all your amazing work!
Zvi


user: ZviBaratz
issue_8181: state: open
title: Unreactive tools until RangeTool overlay is toggled
body: I'm trying to build a small app to do data analysis. I have included a tool to fit a range of the data to a curve. To do so I click on a button which makes the BoxAnnotation for the RangeTool visible, then select the range and then click again to do the fitting.

Initially I want to have the BoxAnnotation hidden so as soon as I create the RangeTool I make it invisible by doing `range_tool.overlay.visible=False`

Now when I start the server all the tools are unreactive until I toggle the overlay once. From then onwards they all work fine. Below is some minimal code that reproduces the issue. If the overlay is not initially hidden then everything works.

As an aside, related to #8180, the HoverTool won't work on the BoxAnnotation area even when the former is hidden.

Thanks,
Guillem

Bokeh Version 0.13.0

```
from bokeh.io import curdoc
from bokeh.layouts import row
from bokeh.models import RangeTool, ColumnDataSource, Range1d
from bokeh.models.widgets import Button
from bokeh.plotting import  output_file, show, figure
from numpy import linspace, cos, pi

# Prepare data
x = linspace(0, 4)
y = cos(2*pi*x)

plot = figure()
plot.line('x', 'y', source=ColumnDataSource(data=dict(x=x, y=y)))

range_tool = RangeTool(x_range=Range1d(1, 2))
range_tool.overlay.fill_alpha = 0.2
plot.add_tools(range_tool)
range_box = plot.tools[-1].overlay

# comment me out to make it work 
# range_box.visible = False

btn = Button(label="Toggle Box Range")
def btn_handler():
        range_box.visible = not range_box.visible
btn.on_click(btn_handler)

curdoc().add_root(row(plot, btn))
```
user: GCBallesteros
issue_8170: state: open
title: Feature request: ZoomOutTool doesn't have a "Maintain Focus" option
body: The WheelZoomTool has an option `maintain_focus` which allows a different than default behavior for zooming out when the bounds of a range are hit by one side of the view, added in [#7683](https://github.com/bokeh/bokeh/pull/7683). It would be useful if this flag was added to the ZoomOutTool as well.
user: TimEwing
issue_8162: state: open
title: embed.components not adding widgets as roots in output
body: Bokeh 0.13 with python 3.6 (anaconda) 64bit on Win10.

I tried using the example from bokeh/examples/embed/embed_multiple.py to embed bokeh components into a static html page and it works.

However, when I add CustomJS interactions to the components, only the first one correctly renders in the browser.
The other components are still there but somehow aren't rendered correctly.
Sample code based on examples:

    import io
    from jinja2 import Template
    
    from bokeh.embed import components
    from bokeh.resources import INLINE
    from bokeh.util.browser import view
    
    from bokeh.layouts import column
    from bokeh.models import CustomJS
    from bokeh.models.widgets import Toggle
    from bokeh.core.properties import Int
    from bokeh.util.compiler import CoffeeScript
    
    class TechToggle(Toggle):
    
        __implementation__ = CoffeeScript("""
    import {Toggle, ToggleView} from "models/widgets/toggle"
    import * as p from "core/properties"
    export class TechToggleView extends ToggleView
      render: () -> 
        super()
    
    export class TechToggle extends Toggle 
      type = "TechToggle"
      default_view = TechToggleView
      @define {
        techid:   [ p.Int, 0 ]
      }
    """)
        techid = Int(default=0)
    
    buttonstates = [0,0]
    
    
    toggle0 = TechToggle(techid=0)
    toggle1 = TechToggle(techid=1)
    #toggles = [toggle0,toggle1]
    #toggles = {0:toggle0, 1:toggle1}
    #toggles=dict(toggle0=toggle0,toggle1=toggle1)
    togglecallback0 = CustomJS(args=dict(a=[toggle0,toggle1]), code="""
           console.log(cb_obj.active)  
           console.log(a)
           console.log(cb_obj)
           if (cb_obj.active == true) { 
               a[1].disabled=true;
           } else {
               a[1].disabled=false;
           }
        """)
    toggle0.callback = togglecallback0
    togglecallback1 = CustomJS(args=dict(a=[toggle0,toggle1]), code="""
           console.log(cb_obj.active)  
           console.log(a)
           console.log(cb_obj)
           if (cb_obj.active == true) { 
               a[0].disabled=true;
           } else {
               a[0].disabled=false;
           }
        """)
    toggle1.callback = togglecallback1
    layout = column(toggle0, toggle1)
    plots = {'Red': toggle0, 'Blue': toggle1}
    
    script, div = components(plots)
    
    template = Template('''<!DOCTYPE html>
    <html lang="en">
        <head>
            <meta charset="utf-8">
            <title>Bokeh Scatter Plots</title>
            {{ resources }}
            {{ script }}
            <style>
                .embed-wrapper {
                    width: 50%;
                    height: 400px;
                    margin: auto;
                }
            </style>
        </head>
        <body>
            {% for key in div.keys() %}
                <div class="embed-wrapper">
                {{ div[key] }}
                </div>
            {% endfor %}
        </body>
    </html>
    ''')
    
    resources = INLINE.render()
    
    filename = 'embed_multiple.html'
    
    html = template.render(resources=resources,
                           script=script,
                           div=div)
    
    with io.open(filename, mode='w', encoding='utf-8') as f:
        f.write(html)
    
    view(filename)

the result looks like:
![renderproblem](https://user-images.githubusercontent.com/1765262/43983018-70fc98e8-9cc6-11e8-9bbc-7e3492ef037f.png)

I suspect this has to do with the passing of models as args in CustomJS.
As shown in the example I've tried a number of signatures for args, however for me they all only render the first button.
Additionally I have also tried rendering with file_html() and it works correctly if the models go through the models input.
However as soon as I attempt to render them through additional jinja variables in the template_variables dict, it again only renders the first button.

My goal was to have customizable interactions between a large number of toggles in various states (this is why the need to know it's own id number) and at the same time have the option to adjust the css styles of them by embedding them into a div as shown in the embed_multiple.py example (`<div class="embed-wrapper">`) so that the css can be individually redefined.

user: bitcom77
issue_8157: state: open
title: Button and TextInput not respecting width
body: The horizontal layout of a TextInput widget and buttons to its left and right produces overlapping elements when widths are specified. The vertical arrangement is also broken. This issue is probably closely related to #6427 but in this case the Button widget is also not respecting the width. I think the vertical alignment is related to #4817.

 Below is the code reproducing the issue.

```
from bokeh.layouts import rowd
from bokeh.models.widgets import TextInput, Button
from bokeh.plotting import  output_file, show

text = TextInput(value="0", width=50)
prev_btn = Button(label="Prev", width=30)
next_btn = Button(label="Next", width=30)

output_file('broken_arrangement.html')
show(row(prev_btn, text, next_btn))
```
Bokeh Version: 0.13.0
Tested on: Firefox 61.0.1, Safari 11.1.1

![screen shot 2018-08-10 at 06 52 21](https://user-images.githubusercontent.com/6859287/43940973-b5e014ea-9c61-11e8-91b9-cee1869bc9bd.png)

I've tested the following combinations of un/setting widths The images below each piece of code correspond to the result.

1) Prev button overlaps text field
```
text = TextInput(value="0")
prev_btn = Button(label="Prev", width=30)
next_btn = Button(label="Next")
```
![screen shot 2018-08-10 at 07 04 43](https://user-images.githubusercontent.com/6859287/43941351-4dfa4a24-9c63-11e8-844e-89edfd3d3c62.png)

2) Text overlaps next button
```
text = TextInput(value="0", width=30)
prev_btn = Button(label="Prev")
next_btn = Button(label="Next")
```
![screen shot 2018-08-10 at 07 06 50](https://user-images.githubusercontent.com/6859287/43941415-982745f2-9c63-11e8-97d2-b487de3905fe.png)


3) Eliminated Text Input. Buttons still overlap if the width of the element to the left is specified.
```
# text = TextInput(value="0")
prev_btn = Button(label="Prev",  width=30)
next_btn = Button(label="Next")
```
![screen shot 2018-08-10 at 07 11 18](https://user-images.githubusercontent.com/6859287/43941578-39ac4ddc-9c64-11e8-8808-6d31b690f0c2.png)

user: GCBallesteros
issue_8150: state: open
title: Problem selecting tools in Windows
body: As described in https://github.com/ioam/holoviews/issues/2901 , tools that work fine in Chrome on Mac OS X are difficult to select and deselect on Windows, taking several clicks and crossing of fingers.
There is a screencast on that page showing the issue on a HoloViews plot, but I have reproduced the same issue on the pure Bokeh plot at the end of https://bokeh.pydata.org/en/latest/docs/user_guide/graph.html#interaction-policies , so it seems to be a Bokeh issue.  Basically, on Mac OS X any click on the tool icon selects or deselects it as normal, but for the same page viewed on the same Chrome version on Windows, it often takes many clicks (e.g. to turn the tap tool off or back on).
user: jbednar
issue_8129: state: open
title: No ability to make true quiver plots
body: So this is an issue that has come up before, such as #977. In #177 arrow annotations were implemented to draw arrows but I do not think that addresses the request for true quiver/vector plots.

Currently there is a gallery called quiver.py on the Bokeh welcome page that shows some flow graphs [here](https://bokeh.pydata.org/en/0.13.0/docs/gallery/quiver.html) but I do not consider this a quiver plot (or a vector field) because we do not see the directionality of the segments, They need an arrow pointing out from one of their end caps. 

Below I show a python example that graphs a vector field in matplotlib and gets close with Bokeh. Notice matplotlib's quiver plotter generates arrows at each point showing direction of the field. Plotly also has a quiver plotter function [seen here](https://plot.ly/python/quiver-plots/). 

But with Bokeh, the closest we can do is use segments. If you run my example, you can see how the Bokeh plot does not help us learn which direction the flow is in, we just see the contour of the flow.

I think implementing this feature will draw in a lot more academics and other users that work with vector fields. Directionality is important as one needs to know if their grid is say shrinking vs expanding.

Here's a couple suggestions for implementation:

1) Add a boolean parameter to the segment glyph called arrow_cap which will turn on an arrow end cap (at x1, y1 pointing out from the segment) for all segments drawn. The size of the arrow will of course need to scale with the line_width of the segment

2) Create a new glyph called quiver that's similar to segment but obviously with one cap being an arrow and the other cap being standard controlled by line_cap. This glyph could implement some automated scaling features and also expect the vector-field standard x, y, u, v data format (where u are v are the deviations at each x, y coordinate) like matplotlib's quiver does. Notice how it took a little more work with segment in my code to get it plotting the data like matplotlib.

Thanks for reading. Hope this was enough info.

```
import matplotlib.pyplot as plt
from bokeh.plotting import figure, curdoc
from bokeh.io import output_file, show
import numpy as np

#create grid then flatten arrays into columns for graphing methods
x, y = np.meshgrid(np.linspace(-10,10,30),np.linspace(-10,10,30))
x = x.ravel()
y = y.ravel()

#create some fun patterns for the deviations (u,v) at each grid point
u = 1/y
v = np.sin(x/5)

# matplotlib quiver plot
fig = plt.figure(figsize = (6,6))
plt.quiver(x,y,u,v)
plt.show()

# get and clear curdoc so can run script multiple times from current python session
doc = curdoc()
doc.clear()

p = figure()
factor = 1/2
p.segment(x, y,
          x + factor * u,
          y + factor * v,
          line_cap = 'round',
          line_width = 4)
output_file('quiver_test.html')
show(p)

```


user: jasonsross
labels: grant: SDG 2023: Funded from NumFOCUS Small Development Grant 2023
issue_8124: state: open
title: Rays get hidden when zooming too much
body: #### Description of the problem

When creating a plot with rays, if I zoom in "too much", rays disappear. Apparently, this happens when zooming small areas which are relatively far from the start of the rays.

**edit** Basically, "infinite" rays gets shorter as you zoom in. Zooming in far from the start point will make the ray be too short to be visible.

#### MWE
Running the following code in jupyter notebook, rays start from 0 and have infinite length upwards. If I , for instance, zoom in the area (0;1), (35,0.5) I can still see the rays being painted, but if zoom in the area (0; 1.01), (35;0.98), rays disappear.

If, after zooming in (0; 1.01), (35;0.98), I pan the view toward the bottom, I can see the rays appearing near y coordinate 0.14. Therefore, rays have an y-extension that goes from 0 to 0.14, instead of 0 to infinity.

Resetting the view will bring back the rays as expected.

```
x = np.arange(50)
data = np.random.rand(x.shape[0])

tags = [1, 17, 31]
colors = 'red green red'.split()

ax = figure(plot_width=300, plot_height=300)

for t, c in zip(tags, colors):
    ax.ray(x=t, y=0, length=0, angle=90, angle_units='deg', color=c)

ax.line(x, data)
show(ax)
```

No errors on JavaScript console.

#### Platform

 - bokeh 0.13.0
 - python 3.6.6
 - jupyter notebook 5.6.0
 - Fedora Linux 27
 - mozilla firefox 61.0.1
user: akiross
issue_8119: state: open
title: Feature request: labels on wedges
body: It would be great if wedges could support displaying labels, so that for example each wedge could show a value, while its associated legend entry showed the name of the series. It seems like this [was possible](https://stackoverflow.com/questions/29201177/how-do-i-create-a-pie-chart-using-bokeh#40914889) in the former `Donut` chart format.
user: scottdotjs
issue_8118: state: open
title: Range1d bounds violation with WheelZoomTool
body: ubuntu/18.04
firefox/61.0.1 and chrome/68.0.3440.75
python/3.6.5
bokeh/0.13.0

Using a WheelZoomTool, it's possible to go outside of bounds that are explicitly set for axis ranges.

It's hard to construct an example with 100% issue reproducibility, but with the following code one can achieve it with a handful of zoom-in -> full zoom-out wheel scroll attempts.
```python
from bokeh.io import curdoc
from bokeh.models import ColumnDataSource, Line, LinearAxis, Plot, Range1d, WheelZoomTool
from tornado import gen

size_x = 600
size_y = 400

x_range = Range1d(0, size_x, bounds=(0, size_x))
y_range = Range1d(0, size_y, bounds=(0, size_y))

main_plot = Plot(x_range=x_range, y_range=y_range)
main_plot.add_tools(WheelZoomTool(maintain_focus=False))

main_plot.add_layout(LinearAxis(), place='below')
main_plot.add_layout(LinearAxis(), place='left')

data_source = ColumnDataSource(dict(x=[0, size_x], y=[0, size_y]))
main_plot.add_glyph(data_source, Line(x='x', y='y'))

@gen.coroutine
def check_plot_range():
    if x_range.start < 0 or x_range.end > size_x or y_range.start < 0 or y_range.end > size_y:
        print(x_range.start, x_range.end, y_range.start, y_range.end)

doc = curdoc()
doc.add_root(main_plot)
doc.add_periodic_callback(check_plot_range, 100)
```

A typical output when the problem occurs would look like:
```
-1.1368683772161603e-13 600 0 399.9999999999987
-1.1368683772161603e-13 600 0 399.9999999999987
-1.1368683772161603e-13 600 0 399.9999999999987
-1.1368683772161603e-13 600 0 399.9999999999987
-1.1368683772161603e-13 600 0 399.9999999999987
```
with one of the values outside of defined borders (here, it's `x_range.start < 0`).

user: ivan-usov
issue_8065: state: open
title: Double arrow displayed on graph node hover tooltip
body: When the hover tooltip occurs on the right hand side of the graph node (using either oval or circle glyph), two arrows appear. When the hover tooltip appears on the left of the glyph, only a single arrow is displayed - as expected.

Error occurs on bokeh 0.12.13, mac osx - using chrome or safari browsers.

```
import math

from bokeh.io import show
from bokeh.plotting import figure
from bokeh.models import GraphRenderer, StaticLayoutProvider,Circle

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="tap,hover", toolbar_location=None)

graph = GraphRenderer()
graph.node_renderer.glyph = Circle(size=15, fill_color="red")
graph.node_renderer.selection_glyph = Circle(size=15, fill_color="blue")
graph.node_renderer.data_source.data = dict(index=node_indices)

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)
show(plot)

```

![graph node hover bug](https://user-images.githubusercontent.com/11044560/42447203-619b0a60-83bc-11e8-9f57-ca3bc00b6b03.gif)



user: anthonydouc
issue_8061: state: open
title: LogTicker broken for colormaps that include zero
body: ref: https://github.com/bokeh/bokeh/issues/6517
ref: https://github.com/bokeh/bokeh/issues/6536

At least for Bokeh 0.13.0 and earlier, adding a LogTicker() to 
[color_data_map.py](https://github.com/bokeh/bokeh/blob/master/examples/plotting/file/color_data_map.py) makes the numerical colorbar labels disappear, making it not usable for the intended purpose of a colorbar (showing the mapping from colors to numerical values):

```
color_bar = ColorBar(color_mapper=mapper, location=(0, 0))
```

![image](https://user-images.githubusercontent.com/1695496/42412685-a69a71e2-81d6-11e8-8c1c-045f15375c1d.png)


```
color_bar = ColorBar(color_mapper=mapper, location=(0, 0), ticker=LogTicker())
```

![image](https://user-images.githubusercontent.com/1695496/42412656-fb8278e0-81d5-11e8-8f00-89279474c502.png)

The problem can be avoided by using a lower bound > 0 on the colorbar:

```
p1 = make_plot(LinearColorMapper(palette=Viridis256, low=1, high=100), title='Viridis256 - Linear, low/high = blue/red')
p2 = make_plot(LogColorMapper(palette=Viridis256, low=1, high=100), title='Viridis256 - Log, low/high = blue/red')
```

![image](https://user-images.githubusercontent.com/1695496/42412813-420c9284-81d9-11e8-8972-bfd1cafd7e36.png)

But because having a lower bound of 0 is not a problem for the actual colormapping, it doesn't seem like it should be an issue for the colorbar's ticker, right?  Presumably the colorbar is using log1p, while the ticker is trying to take the regular log of 0 and failing.  Can we simply have the ticker start at 1 when the colorbar starts at 0?  

You might argue that we should instead be using a lower bound of 1 for the colormapping, but allowing 0 values for colormapping is an extremely important feature for us with datashader and other types of heatmaps that are plotting counts, because we want to use log colormapping to handle large counts but many cells in a heatmap or raster plot are zero because a count of zero is also common. In any case, I think the ticker behavior should match the colormapping behavior, as the purpose of a ticker is to reveal the colormap.
user: jbednar
issue_8053: state: open
title: Exporting GMapPlot using export_png are sometimes grayed out with a Google Maps error
body: #### Version
python  3.6.5
bokeh 0.12.16
selenium 3.12.0 (dependency)
pollow 5.1.0 (dependency)
phantomjs 2.1.1 (dependency)

#### Expected
When exporting a GMapPlot using export_png, each file generated should be a png with a google maps image. Each GMapPlot can also be displayed properly in Jupyter Notebook.

#### Observed
When exporting a GMapPlot using export_png, roughly 3 out of every 5 generated contain a grey screen. Each GMapPlot is still displaying properly in Jupyter Notebook.

Some of these grey screens contain the text "Opps! Something went wrong. This page didn't load Google Maps correctly. See the JavaScript console for technical details.". 

![test 1](https://user-images.githubusercontent.com/40864574/42337624-58f0970a-8055-11e8-8f67-edeaf331489f.png)
![test 2](https://user-images.githubusercontent.com/40864574/42337625-58fb97b8-8055-11e8-9903-8a9b1086453d.png)

I am not seeing any errors in the JavaScript console.

#### Example code to recreate the issue
```
from bokeh.io import output_notebook, show, export_png
from bokeh.models import GMapPlot, GMapOptions, Range1d

output_notebook()

for x in range(10):
    map_options = GMapOptions(lat=39.952583, lng=-75.165222, map_type="terrain", zoom=10)
    plot = GMapPlot(x_range=Range1d(), y_range=Range1d(), map_options=map_options)
    plot.api_key = "API_KEY"
    plot.title.text = "Test %s" % x
    
    export_png(plot, "Test %s.png" % x)    
    show(plot)
```

user: harrisonzalman
issue_8047: state: open
title: Add ability to add title to DataTable
body: Quite often there are multiple tables in a dashboard/layout and you want to distinguish between them. Currently it is somewhat awkward to add a title for the tables so it would be very handy if it were possible to add titles to DataTables to make this easier just and more like regular plot types.
user: philippjfr
issue_8023: state: open
title: `transform.cumsum` doesn't take into account the CDSView
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
bokeh 0.13
python 3.6.5
OS linux
browser Firefox

#### Description of expected behavior and the observed behavior
In the example below, 2016 renderer is shifted because of 2015 values.

#### Complete, minimal, self-contained example code that reproduces the issue

```
from bokeh.io import show, output_file
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource, CDSView, GroupFilter
from bokeh.transform import factor_cmap
from bokeh.transform import cumsum

output_file('test_cumsum_cdsview.html')

status_cmap = factor_cmap('status',
                          palette=['blue', 'green', 'yellow', 'orange', 'red', 'grey'],
                          factors=['1', '2', '3', '4', '5', 'U'])

fig = figure(x_range=['2015', '2016'],
             #y_range=(0, 100),
             plot_width=300,
             plot_height=300,
             tools="reset,save")
source12 = ColumnDataSource(
    {'status': ['1', '2', '3', '4', '5', 'U', '1', '2', '3', '4', '5', 'U'],
     'value': [10, 20, 30, 5, 5, 30, 10, 35, 15, 15, 15, 10],
     'year': ['2015', '2015', '2015', '2015', '2015', '2015', '2016', '2016', '2016', '2016', '2016', '2016']
    })

group_filter_2015 = GroupFilter(column_name='year', group='2015')
group_filter_2016 = GroupFilter(column_name='year', group='2016')
view_2015 = CDSView(source=source12, filters=[group_filter_2015])
view_2016 = CDSView(source=source12, filters=[group_filter_2016])
fig.vbar(x='year', width=0.5,
         bottom=cumsum('value', include_zero=True), top=cumsum('value'),
         color=status_cmap,
         source=source12, view=view_2015)
fig.vbar(x='year', width=0.5,
         bottom=cumsum('value', include_zero=True), top=cumsum('value'),
         color=status_cmap,
         source=source12, view=view_2016)

show(fig)

```

#### Stack traceback and/or browser JavaScript console output

#### Screenshots or screencasts of the bug in action
![image](https://user-images.githubusercontent.com/25739150/41809196-939213cc-76e9-11e8-9b91-9376b3910cd4.png)

user: lgasnier
issue_8012: state: open
title: Ability for sliders to have log scale
body: This is a feature request, as I haven't determined how I can do this yet.

I have a bokeh graph, with a y axis that is log scale, ans x axis that is linear/normal scale.  I have two RangeSliders that allow for filtering on the graph based on the values of the sliders.  The RangeSliders are always linear, so it can be hard to select smaller values, since step and the scale are all linear.  Is there any way to be able to specify a scale, kind of like how axis is done, for a slider, that would allow the value of the slider to match with a log scale axis?
user: cbanek
issue_8010: state: open
title: color mapping in legend does not take current view (CDSView) into account and color mapping in circle fill is wrong after subsampling with box_select from another plot
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
bokeh 0.12.16
python 3.6.5
notebook 5.5.0
OSX 10.11.6
browser safari or chrome
pandas 0.23.0

#### Description of expected behavior and the observed behavior

Here is a small dataset:
x_values	y_values	fruit	animal
1	1	apple	cat
2	0	pear	cat
3	1	apple	dog
4	0	apple	bird
5	1	kiwi	cat

I would expect that if I view a subset of the data using CDSView the legend will still match the colors to the unique list of factors. I would also expect that if a plot like plot B (created from a CDSView) was filtered using the box_select tool in plot A the color mapping would adjust and still reflect the correct color.

In the example below the legend is only correct when the data is not a CDSView subset of the full ColumnDataSource. The legend is only correct in plots A and C.
<img width="618" alt="screen shot 2018-06-18 at 10 02 14 pm" src="https://user-images.githubusercontent.com/14060349/41571893-5696b062-7343-11e8-9059-762cbe100090.png">

Also in the example below if I use box select on figure A then the color mapping shifts in figure B. 
<img width="636" alt="screen shot 2018-06-18 at 10 02 26 pm" src="https://user-images.githubusercontent.com/14060349/41571901-5927ad4a-7343-11e8-9634-b51f703c073b.png">



#### Complete, minimal, self-contained example code that reproduces the issue

```
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource, CDSView, BooleanFilter, CategoricalColorMapper
from bokeh.layouts import gridplot
from bokeh.io import show

data = {'x_values': [1, 2, 3, 4, 5],
        'y_values': [1, 0, 1, 0, 1],
        'fruit' : ['apple', 'pear', 'apple', 'apple', 'kiwi'],
        'animal' : ['cat', 'cat', 'dog', 'bird', 'cat']}

color_mapper = CategoricalColorMapper(factors=['apple', 'pear', 'kiwi'], 
                                          palette=['#ffd92f', '#20b2aa', '#e78ac3'])

color_mapper2 = CategoricalColorMapper(factors=['cat', 'dog', 'bird'], 
                                          palette=['red', 'black', 'yellow'])

source = ColumnDataSource(data=data)

booleans = [True if val == 1 else False for val in source.data['y_values']]

view1 = CDSView(source=source, filters=[BooleanFilter(booleans)])

# Figure to select ColumnDataSource rows from woth box_select
tools = ["box_select", "reset"]
p1=figure(tools=tools,
          plot_width=400,
          plot_height=400,
         title="A. Correct fill colors and legend")
p1.circle("x_values","y_values",
          legend='fruit',
          fill_color={'field': 'fruit',
                      'transform': color_mapper},
          size=20, 
          source=source)

# Figure that is a CDS view of the source data
p=figure(plot_width=400,
        plot_height=400,
        title="B. Good fill colors until box_select is used and bad legend")
p.circle("x_values", "y_values", size=20, source=source, view=view1, 
         legend='animal',
         fill_color={'field': 'animal',
                      'transform': color_mapper2},)
# Figure that includes all the source data (no view)
p_full=figure(plot_width=400,
          plot_height=400,
        title="C. Correct fill colors and legend even after box_select")
p_full.circle("x_values", "y_values", size=20, source=source, 
         legend='animal',
         fill_color={'field': 'animal',
                      'transform': color_mapper2},)

p1.legend.location = 'center_right'
p_full.legend.location = 'center_right'
show(gridplot([[p1,p],
             [p_full]]))

```

#### Stack traceback and/or browser JavaScript console output

#### Screenshots or screencasts of the bug in action
user: JenniferShelton
issue_7988: state: open
title: Add ability to insert new row in DataTable
body: In certain cases it would be very helpful to be able to insert new rows in a DataTable. The data table can be very helpful in annotating data and this feature could make this process a lot easier. The main issue will be finding a nice UI for adding a row since Slickgrid does not provide one afaik. @mattpap previously mentioned this would be reasonably straightforward.

Edit: (bev) It would make sense to implement delete row at the same time as part of the same work. 
user: philippjfr
issue_7970: state: open
title: Error message on the browser console when using CustomJS with args in serve mode
body: Bokeh 0.12.16, tornado 5.0.2, Python 3.6.5 on Linux, tried both Firefox 60 and Chromium 67

If run with
bokeh **html** --show example.py
everything runs as expected (on the browser console we can see no errors).

If run with
bokeh **serve** --show example.py
we get an error message on the browser console, e.g.
`Error: reference {"id":"2e235317-bf21-4a8c-9057-6d1801ded9b8","type":"DataRange1d"} isn't known (not in Document?)`

Both correctly install the CustomJS handler and show updates to the x_range in the browser console.

``` python
from bokeh.plotting import figure
from bokeh.layouts import layout
from bokeh.models import Div, CustomJS
from bokeh.io import curdoc

# prepare some data
x = [1, 2, 3, 4, 5]
y = [6, 7, 2, 4, 5]

# create a new plot with a title and axis labels
p = figure(title="simple line example", x_axis_label='x', y_axis_label='y')

# add a line renderer with legend and line thickness
p.line(x, y, legend="Temp.", line_width=2)

div = Div()
code = """
        console.log('' + range.start + ' to ' + range.end);
        """

p.x_range.callback = CustomJS(args=dict(range=p.x_range), code=code)

l = layout([[p]])

curdoc().add_root(l)
```
user: thomasetter
issue_7969: state: open
title: MultiLine hover shows all values for xs, ys
body: This is has come up twice no SO. 

refs:
 
https://stackoverflow.com/questions/50219290/bokeh-hovertool-tooltips-for-multi-line-displaying-all-coordinates-at-all-poin
https://stackoverflow.com/questions/50709914/how-to-use-hover-with-multi-line-in-bokeh/

The hover code should do something similar to the new image hover code, and inspect the column data shape. If it is a list of scalars, use the top level index. But if it is a "list of lists" drill down to get the item for the specific point on the specific line. 
user: bryevdv
issue_7913: state: open
title: Hover tool still shows up for hidden line glyphs
body: A hover label shows up for line glyphs when they're hidden by a filter. This is unexpected since it doesn't happen with scatter glyphs. Here's a little example:

```python
from bokeh.models import ColumnDataSource, CDSView, BooleanFilter
from bokeh.plotting import figure, output_file, show
from itertools import repeat, chain

output_file("plot.html")

source = ColumnDataSource({
    "x": list(range(10)),
    "y": list(range(10))
})
filt = BooleanFilter(list(repeat(False, 10)))
view = CDSView(source=source, filters=[filt])

p = figure(tools=["hover"])
p.line(x="x", y="y", source=source, view=view)
show(p)
```

Output in `gif` form:

![bokehissue_hover2](https://user-images.githubusercontent.com/8238804/40112704-455f0abe-594a-11e8-88b3-695b711d33e3.gif)

Using `Python 3.6.5` and `bokeh 0.12.16`, checked in both Chrome and Safari.




user: ivirshup
issue_7852: state: open
title: Create optional bundle for IonRangeSlider from the examples
body: Create an optional bundle for the IonRangeSlider, as discussed in https://github.com/bokeh/bokeh/pull/7850#issuecomment-385771880. Relevant snips:

> I would say ionRangeSlider is definitely a better slider in almost all respects than noUiSlider, in terms of appearance, configurability, and features, with the sole exception of lacking a vertical orientation. Unfortunately I don't really think we can replace it as the default slider at this point, but I would not be against adding at as another optional bundle so that users don't have to resort to copy and pasting a custom extension just to use it.

> I doubt there is much by way of documentation on this, it's somewhat tied to our build/release process which has mostly only involved a couple of people. That said perhaps this is an opportunity to improve the dev docs. In terms of previous models, you can look to how the bokeh tables widgets bundle was split off.

> Well, that happened in this PR, which was unfortunately rather large, but individual commits may point to how it worked to split things up: #6335
user: Karel-van-de-Plassche
issue_7789: state: open
title: Ability to embed axes separately from plots
body: Created from a Gitter discussion upon the request from @mattpap 

With the current plotting system, it's impossible to embed Bokeh plots into an existing UI and have aligned axes without implementing custom Bokeh models.

Example: I want to have two plots, one above the other, in a horizontal adjustable splitter. And I need Y-axes of the plots to always align.

A workaround is possible - one can create a separate plot with a static width, and leave only the Y-axis visible. The downside is obvious though - the solution works only for plots with constant Y-ranges. As soon as a user starts to scroll vertically one of the plots, the width of the Y-axis becomes larger than the specified static width, which causes solver errors.

In my opinion, the ideal solution would be to refactor `Plot` model and all related components into much more smaller components, while trying to incorporate SRP. If done correctly, it would allow for easy implementation of some kind of `AxisPlot` model that could be used along with regular plots.
A more conservative and safe approach would be to create a separate `AxisPlot` model without changing the existing `Plot` model significantly.

user: p-himik
issue_7775: state: open
title: Point draw tool not working with projected coordinate systems 
body: The point draw tool does not seem to be working with the projected coordinate systems. The tool is returning WebMercator northings and eastings (meters), which is the underlying cartesian coordinate system that google maps uses. But the glyph assumes its inputs are in lat/lon and still need conversion to WebMercator, so the effect would seem to be that the values are erroneously projected twice.
#
The below is example code modified to allow the PointDrawTool. I have added a table to the bottom that adds the returned x & y. 

Additionally, the documentation does not seem to show how to add the glyph upon selection. 
```
import pandas as pd
from bokeh.io import show
from bokeh.models import ColumnDataSource as cds, GMapOptions, TableColumn, DataTable
from bokeh.plotting import gmap
from bokeh.layouts import column,layout
from bokeh.models.glyphs import Circle

map_options = GMapOptions(lat=30.2861, lng=-97.7394, map_type="roadmap", zoom=11)

# For GMaps to function, Google requires you obtain and enable an API key:
#
#     https://developers.google.com/maps/documentation/javascript/get-api-key
#
# Replace the value below with your personal API key:
p = gmap("APIKey", map_options, title="Austin")

source = ColumnDataSource(
    data=dict(lat=[ 30.29,  30.20,  30.29],
              lon=[-97.70, -97.74, -97.78],
             name = ['1', '2', '3'])
)

c1 = p.circle(x="lon", y="lat", size=15, fill_color="blue", fill_alpha=0.8, source=source)

columns = [TableColumn(field='lon', title="x"),
           TableColumn(field='lat', title="y"),
           TableColumn(field='name', title='Name')]

table = DataTable(source=source, columns=columns, editable=True, height=200)

draw_tool = PointDrawTool(renderers=[c1], empty_value='added', add=True, )
p.add_tools(draw_tool)
p.toolbar.active_tap = draw_tool

col = column(p, table)
show(col)
```

user: chadasmithphd
issue_7772: state: open
title: Settting step property of Slider in Python doesn't update the step in Javascript correctly
body: It seems that the step property of slider only be used when the slider widget is created on the browser side. 

A change to the step property in a callback function will make the arrow key action weird.

Here is the sample code, change the step to 5, and then click the slider handle, and press arrow key, the value of the slider can't change correctly any more.

```
from bokeh.plotting import curdoc
from bokeh.models import Slider, Select
from bokeh.layouts import row

step_select = Select(title="step", options=["1", "2", "5"])
slider = Slider(title="value", start=0, end=100, step=1, value=0)

def step_callback(attr, old, new):
    slider.step = int(new)

step_select.on_change("value", step_callback)

curdoc().add_root(row(step_select, slider))
```

![slider](https://user-images.githubusercontent.com/826871/38477547-2e962368-3bee-11e8-869b-c20c00bd63ba.gif)


current I use the following code to walk around this problem. recreate the the widget everytime the step property is changed.

```
from bokeh.plotting import curdoc
from bokeh.models import Slider, Select
from bokeh.layouts import row

step_select = Select(title="step", options=["1", "2", "5"])
slider = Slider(title="value", start=0, end=100, step=1, value=0, width=500)
slider_parent = row(slider)

def step_callback(attr, old, new):
    slider.step = int(new)
    slider_parent.children = []
    def update():
        slider_parent.children = [slider]
    curdoc().add_next_tick_callback(update)
    
step_select.on_change("value", step_callback)    

curdoc().add_root(row(step_select, slider_parent))
```

user: ruoyu0088
issue_7668: state: open
title: Tap tool does not work as expected with line glyphs
body: When the tap tool is selected and the user clicks on a line, no point is selected, but the callback (e.g. OpenURL) will fire not with the nearest point, but rather the one on the left.

This is easy to reproduce with a simple modification to the [OpenURL example](https://bokeh.pydata.org/en/latest/docs/user_guide/interaction/callbacks.html#openurl):

```
from bokeh.models import ColumnDataSource, OpenURL, TapTool
from bokeh.plotting import figure, output_file, show

output_file("openurl.html")

p = figure(plot_width=400, plot_height=400,
           tools="tap", title="Click the Dots")

source = ColumnDataSource(data=dict(
    x=[1, 2, 3, 4, 5],
    y=[2, 5, 8, 2, 7],
    color=["navy", "orange", "olive", "firebrick", "gold"]
    ))

p.circle('x', 'y', color='color', size=20, source=source)
# breaks tap tool behavior
p.line('x', 'y', color='black', source=source)

# use the "color" column of the CDS to complete the URL
# e.g. if the glyph at index 10 is selected, then @color
# will be replaced with source.data['color'][10]
url = "http://www.colors.commutercreative.com/@color/"
taptool = p.select(type=TapTool)
taptool.callback = OpenURL(url=url)

show(p)
```

In the above, example, clicking exactly within one of the large circles will open the correct color URL. Clicking on the line, even immediately next to the gold (right-most) circle, will open the fire brick page (which is the point to the left). It's "floor-ing" to select the point to open a URL for, instead of choosing the nearest one.

The hover tool works as expected with a combination of line and circle. In that case, when hovering on the line, as you pass the midpoint between two circles, the tag jumps to the nearest one. Tap tool should follow this behavior.

The issue is that, when you create a line plot with small markers at each data point, the combination of hover and tap is confusing, because hover will show the right tag, but tap will open the wrong URL.
user: shaperilio
issue_7667: state: open
title: Zooming in x-axis in doesn't work when there're bounds on y-axis
body: Bokeh 0.12.15dev5

```python
from bokeh.models import Range1d, WheelZoomTool
from bokeh.plotting import figure, save, output_file

wheel_zoom = WheelZoomTool(dimensions='width')
fig = figure(x_range=Range1d(0, 1),
             y_range=Range1d(0, 1),
             tools=[wheel_zoom],
             # circumventing not working `active_scroll='auto'`
             active_scroll=wheel_zoom)
fig.y_range.bounds = (0, 1)
fig.x([0.1, 0.9], [0.1, 0.9], size=10)
```

Zooming with `dimensions='width'` should change only `x_range`, so it shouldn't be affected by the bounds set on `y_range`. However, this is not the case - with this code, zooming just doesn't work. Removing the bounds or explicitly set dimensions makes everything work again.

user: p-himik
issue_7644: state: open
title: Expose options to suppress linked selection/inspection when data sources are shared
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
```
bokeh.__version__
'0.12.13'
```

Python 3.6.4, Mac OS 10.13.3, Firefox 59.0.1

#### Description of expected behavior and the observed behavior
Hover properties such as `hover_fill_alpha`, `hover_color`, etc, only apply when the hover tool is above the topmost stack of a stacked bar. Hovering over any other stack does not apply any of the properties.

Tested using both `vbar_stack` and `hbar_stack`.

It would be *awesome* if the hover could apply per-stack, as opposed to the entire bar at once, but that's a story for another day.

#### Complete, minimal, self-contained example code that reproduces the issue

```
import bokeh
from bokeh.plotting import figure, show
from bokeh.io import output_notebook
from bokeh.models.tools import HoverTool
from bokeh.models import ColumnDataSource
from bokeh.core.properties import value

hover = HoverTool(tooltips=[('fruit', '@fruits')])

fruits = ['Apples', 'Pears', 'Nectarines', 'Plums', 'Grapes', 'Strawberries']
years = ["2015", "2016", "2017"]
colors = ["#c9d9d3", "#718dbf", "#e84d60"]

data = {'fruits' : fruits,
        '2015'   : [2, 1, 4, 3, 2, 4],
        '2016'   : [5, 3, 4, 2, 4, 6],
        '2017'   : [3, 2, 4, 4, 5, 3]}

source = ColumnDataSource(data=data)

p = figure(x_range=fruits, plot_height=250, title="Fruit Counts by Year",
           toolbar_location=None, tools=[hover])

p.vbar_stack(years, x='fruits', width=0.9, color=colors, source=source,
             legend=[value(x) for x in years], hover_fill_alpha=0.8, hover_color=colors)
```

#### Screenshots or screencasts of the bug in action
![broken](https://user-images.githubusercontent.com/35412753/37559406-a594c3e6-29f3-11e8-8ffb-b768e3b0fb42.png)
![working](https://user-images.githubusercontent.com/35412753/37559407-a5a150fc-29f3-11e8-81b2-07554dcc1c29.png)

## I'd be interested in fixing it, if someone can point me towards the appropriate files as a starting point.
user: atomic-mkdir
issue_7607: state: open
title: Single Legend for GlyphRenderers on Multiple Plots
body:  I have tried to make this work

    from bokeh.models import ColumnDataSource, CDSView, IndexFilter
    from bokeh.palettes import Reds3
    from bokeh.layouts import row
    from bokeh.plotting import curdoc, figure
    from bokeh.models.annotations import Legend
    import numpy as np

    plot_1 = figure(
        width=300,
        height=300,
        tools="pan,box_zoom,box_select,wheel_zoom,tap,save,reset,crosshair",
        toolbar_location='left',
    )

    plot_2 = figure(
        width=430,
        height=300,
        tools="pan,box_zoom,box_select,wheel_zoom,tap,save,reset,crosshair",
        toolbar_location='left',
    )

    x = list(range(0,10))
    y = np.random.random_integers(200, size=(10))
    l = np.random.random_integers(3, size=(10))

    x2 = list(range(0,10))
    y2 = np.random.random_integers(200, size=(10))
    l2 = np.random.random_integers(3, size=(10))

    source = ColumnDataSource(data=dict(x=x, y=y, x2=x2, y2=y2))

    view_1 = CDSView(
        source=source,
        filters=[IndexFilter([0, 1, 2, 3])]
    )

    view_2 = CDSView(
        source=source,
        filters=[IndexFilter([5, 6, 7])]
    )

    view_3 = CDSView(
        source=source,
        filters=[IndexFilter([8, 9, 4])]
    )

    circle_1 = plot_1.circle(
        x='x',
        y='y',
        size=5,
        fill_alpha=1,
        line_color=None,
        fill_color=Reds3[0],
        source=source,
        view=view_1,
    )

    circle_2 = plot_1.circle(
        x='x',
        y='y',
        size=5,
        fill_alpha=1,
        line_color=None,
        fill_color=Reds3[1],
        source=source,
        view=view_2,
    )

    circle_3 = plot_1.circle(
        x='x',
        y='y',
        size=5,
        fill_alpha=1,
        line_color=None,
        fill_color=Reds3[2],
        source=source,
        view=view_3,
    )

    view_4 = CDSView(
        source=source,
        filters=[IndexFilter([0, 1, 2, 3])]
    )

    view_5 = CDSView(
        source=source,
        filters=[IndexFilter([5, 6, 7])]
    )

    view_6 = CDSView(
        source=source,
        filters=[IndexFilter([8, 9, 4])]
    )

    circle_4 = plot_2.circle(
        x='x',
        y='y',
        size=5,
        fill_alpha=1,
        fill_color=Reds3[0],
        line_color=None,
        source=source,
        view=view_4,
    )

    circle_5 = plot_2.circle(
        x='x',
        y='y',
        size=5,
        fill_alpha=1,
        fill_color=Reds3[1],
        line_color=None,
        source=source,
        view=view_5,
    )

    circle_6 = plot_2.circle(
        x='x',
        y='y',
        size=5,
        fill_alpha=1,
        fill_color=Reds3[2],
        line_color=None,
        source=source,
        view=view_6,
    )

    legend = Legend(
        items=[
            ("LEGEND 1", [circle_4]),
            ("LEGEND 2", [circle_5]),
            ("LEGEND 3", [circle_3, circle_6]),  # renderers for several plots
        ],
        location=(10, 0)
    )
    plot_2.add_layout(legend, 'right')
    plot_2.legend.click_policy = 'hide'

    curdoc().add_root(row([plot_1, plot_2]))

![](https://s10.postimg.org/z6o7g43p5/legend.png)

I think this is not possible because the legend is usually added to one plot `plot.add_layout(legend, 'right')`. Anyway I have run an example and the glyphs have become invisible on both plots. But I get this error on JavaScript `Cannot read property 'draw_legend' of undefined` and the color on the legend box is not visible.

So I write this as a feature request. I think It would be useful to have more flexibility on the Legends objects.
user: chesucr
issue_7524: state: open
title: GroupFilter only accepts strings
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)

bokeh 0.12.14

#### Description of expected behavior and the observed behavior

GroupFilter only operates on string-valued columns

#### Complete, minimal, self-contained example code that reproduces the issue

```python
from bokeh.models import GroupFilter

GroupFilter(column_name='visible', group=True)
```

### Traceback

<details>

```python
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-30-b872a7a9ba79> in <module>()
      1 
----> 2 GroupFilter(column_name='visible', group=True)

~/anaconda/lib/python3.6/site-packages/bokeh/models/filters.py in __init__(self, *args, **kw)
     72             kw["group"] = args[1]
     73 
---> 74         super(GroupFilter, self).__init__(**kw)
     75 
     76 class CustomJSFilter(Filter):

~/anaconda/lib/python3.6/site-packages/bokeh/models/filters.py in __init__(self, *args, **kw)
     23             kw["filter"] = args[0]
     24 
---> 25         super(Filter, self).__init__(**kw)
     26 
     27 class IndexFilter(Filter):

~/anaconda/lib/python3.6/site-packages/bokeh/model.py in __init__(self, **kwargs)
    217         self._id = kwargs.pop("id", make_id())
    218         self._document = None
--> 219         super(Model, self).__init__(**kwargs)
    220         default_theme.apply_to_model(self)
    221 

~/anaconda/lib/python3.6/site-packages/bokeh/core/has_props.py in __init__(self, **properties)
    234 
    235         for name, value in properties.items():
--> 236             setattr(self, name, value)
    237 
    238     def __setattr__(self, name, value):

~/anaconda/lib/python3.6/site-packages/bokeh/core/has_props.py in __setattr__(self, name, value)
    261 
    262         if name in props or (descriptor is not None and descriptor.fset is not None):
--> 263             super(HasProps, self).__setattr__(name, value)
    264         else:
    265             matches, text = difflib.get_close_matches(name.lower(), props), "similar"

~/anaconda/lib/python3.6/site-packages/bokeh/core/property/descriptors.py in __set__(self, obj, value, setter)
    501             raise RuntimeError("%s.%s is a readonly property" % (obj.__class__.__name__, self.name))
    502 
--> 503         self._internal_set(obj, value, setter=setter)
    504 
    505     def __delete__(self, obj):

~/anaconda/lib/python3.6/site-packages/bokeh/core/property/descriptors.py in _internal_set(self, obj, value, hint, setter)
    722 
    723         '''
--> 724         value = self.property.prepare_value(obj, self.name, value)
    725 
    726         old = self.__get__(obj, obj.__class__)

~/anaconda/lib/python3.6/site-packages/bokeh/core/property/bases.py in prepare_value(self, obj_or_cls, name, value)
    279                     break
    280             else:
--> 281                 raise e
    282         else:
    283             value = self.transform(value)

~/anaconda/lib/python3.6/site-packages/bokeh/core/property/bases.py in prepare_value(self, obj_or_cls, name, value)
    272     def prepare_value(self, obj_or_cls, name, value):
    273         try:
--> 274             self.validate(value)
    275         except ValueError as e:
    276             for tp, converter in self.alternatives:

~/anaconda/lib/python3.6/site-packages/bokeh/core/property/bases.py in validate(self, value)
    403         if not (value is None or isinstance(value, self._underlying_type)):
    404             raise ValueError("expected a value of type %s, got %s of type %s" %
--> 405                 (nice_join([ cls.__name__ for cls in self._underlying_type ]), value, type(value).__name__))
    406 
    407     def from_json(self, json, models=None):

ValueError: expected a value of type str, got True of type bool
```
</details>
user: mrocklin
issue_7490: state: open
title: Performance benchmark with many points
body: I'm testing how many points I can reasonably place on a plot and still get decent performance.  This is for possible future Dask dashboards.  Here is a simple bokeh server application that streams points onto a plot at around 100/second

```python
from bokeh.server.server import Server
from bokeh.plotting import figure, ColumnDataSource
import random

def handle_doc(doc):
    source = ColumnDataSource({'x': [], 'y': []})
    fig = figure()
    fig.scatter(x='x', y='y', source=source)

    def f():
        source.stream({'x': [random.random() for _ in range(10)],
                       'y': [random.random() for _ in range(10)]})

    doc.add_periodic_callback(f, 100)

    doc.add_root(fig)

apps = {'/': handle_doc}

server = Server(apps, port=5000)
server.start()

import tornado.ioloop
tornado.ioloop.IOLoop.current().start()
```

Bokeh server handles this fine.  Chrome on the other hand reaches 100% cpu use within ten seconds.  Firefox within twenty five.
user: mrocklin
issue_7477: state: open
title: Numbro's languages.js is not bundled
body: Bokeh 0.12.14dev4

Related issue: https://github.com/bokeh/bokeh/issues/7124
Started from: https://groups.google.com/a/continuum.io/forum/?utm_medium=email&utm_source=footer#!topic/bokeh/_Fg6UopTP9c

I think that always bundling all languages is not necessary since it's another 0.5MB, but some note to users when they use `NumeralTickFormatter(language="...")` is needed. Or maybe an automatic inclusion of necessary third-party JS files is possible when `language` attribute is set to something other than default `en-US`.
user: p-himik
issue_7438: state: open
title: Bokeh rendering glyphs in inactive tabs
body: After a lot of frustration, I finally found a solution to my performance issues with Bokeh in my server web app. I was having tremendous slowdowns that only increased when I added hovertools with custom tooltips to each of the plots I had under a tab.

I was able to return the performance to a very good state by having a JS Callback in the tab widget set the .visible property of all GlyphRenderers to false if they weren't behind the active tab. This is something that the widget should be doing by default IMO. 
user: Charles75382
issue_7419: state: open
title: Hover tooltip not showing for step line
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
Python 3.6
Bokeh 0.12.13

#### Description of expected behavior and the observed behavior
Using the default hover tool I do not see the tooltip when using the step line. Switching to circle or line will result to the correct behaviour.

#### Complete, minimal, self-contained example code that reproduces the issue

```
from bokeh.plotting import figure, output_file, show

# output to static HTML file
output_file("test.html")

p = figure(plot_width=400,
           plot_height=400,
           tools='hover')

# p.circle([1, 2, 3, 4, 5], [6, 7, 2, 4, 5],
#          size=20,
#          color="navy",
#          alpha=0.5,
#          )

p.step([1, 2, 3, 4, 5], [6, 7, 2, 4, 5],
         color="navy",
         alpha=0.5,
         )

# p.line([1, 2, 3, 4, 5], [6, 7, 2, 4, 5],
#        color="navy",
#        alpha=0.5,
#        )

# show the results
show(p)
```


user: mjmare
issue_7408: state: open
title: Hover displaying datetime using wrong locale
body: *Disclaimer*: I am new to Bokeh but I like it alot! Please consider that I am just doing something wrong ;)

When using datetime objects within a hover then in my understanding it should be displayed using the current locale settings when using format modifier "%c".
 According to: https://bokeh.pydata.org/en/latest/docs/reference/models/formatters.html#bokeh.models.formatters.DatetimeTickFormatter.

So as I am in germany and locale is german, time should be displayed as for example "21:20:37" (no AM/PM). Instead it uses a US format (AM/PM):
![image](https://user-images.githubusercontent.com/364469/34908289-77c13b76-f88d-11e7-87dd-bb60d958b9de.png)

As an example I modified the candlestick example to show a hover:
```
from math import pi
from bokeh.models import HoverTool

import pandas as pd

from bokeh.plotting import figure, show, output_file
from bokeh.sampledata.stocks import MSFT

df = pd.DataFrame(MSFT)[:50]
df["date"] = pd.to_datetime(df["date"])

inc = df.close > df.open
dec = df.open > df.close
w = 12*60*60*1000 # half day in ms

TOOLS = "pan,wheel_zoom,box_zoom,reset,save"

p = figure(x_axis_type="datetime", tools=TOOLS, plot_width=1000, title = "MSFT Candlestick")
p.xaxis.major_label_orientation = pi/4
p.grid.grid_line_alpha=0.3

h = HoverTool(tooltips=[('Time', '@date{%c}')],
              mode="vline",
              formatters={'date': 'datetime'}
              )
p.tools.append(h)

p.segment(df.date, df.high, df.date, df.low, color="black")
p.vbar('date', w, 'open', 'close', fill_color="#D5E1DD", line_color="black", source=df)

output_file("candlestick.html", title="candlestick.py example")

show(p)  # open a browser
```

*Environment:*
Windows 10
Python 3.6.3 Anaconda
Bokeh 0.12.13
Google Chrome

So do I manually have to set the locale I want to use? In my understanding it should be auto detected? Thanks for your time and for Bokeh!


user: verybadsoldier
issue_7397: state: open
title: hover_color only works for the last GlyphRenderer
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)

Bokeh  0.12.13, Python 3.5, notebook 5.2.1, windows 10, Chrome

#### Complete, minimal, self-contained example code that reproduces the issue

```
from bokeh.models import ColumnDataSource
from bokeh.plotting import figure
from bokeh.io import show, output_notebook
output_notebook()

source = ColumnDataSource(data=dict(x=[1, 2, 3], y0=[1, 2, 3], y1=[-1, -2, -3]))
fig = figure(tools="hover", plot_width=500, plot_height=300, title=None)
c1 = fig.circle('x', 'y0', size=10, hover_color="firebrick", source=source)
c2 = fig.circle('x', 'y1', size=10, hover_color="firebrick", source=source)
show(fig)
```

#### Screenshots or screencasts of the bug in action

when mouse is over c1:

![chrome_2018-01-11_12-53-43](https://user-images.githubusercontent.com/826871/34807937-b744168a-f6ce-11e7-9b9d-e0343c2752c6.png)

when mouse is over c2:

![chrome_2018-01-11_12-54-18](https://user-images.githubusercontent.com/826871/34807943-c0dfbdfc-f6ce-11e7-9967-614a60d192e6.png)

user: ruoyu0088
issue_7357: state: open
title: Interactively changing the title of a tab pane
body: #### software version info
python 3.5.2
bokeh 0.12.7
numpy 1.11.1

#### Description of expected behavior and the observed behavior
I want to interactively change the title of a Tab using this line of code:
tabname.title = newtitle
When I click on the button ENG/DE in my example code the titles are supposed to change. Unfortunately this does not work as expected. The title will not update.
My example demonstrates the issue in contrast to other widgets, which update their title just fine.

#### Complete, minimal, self-contained example code that reproduces the issue
```
import numpy as np

from bokeh.io import curdoc
from bokeh.layouts import widgetbox, layout
from bokeh.models import ColumnDataSource
from bokeh.models.widgets import Slider, TextInput
from bokeh.models.widgets import Panel, Tabs, CheckboxGroup, RadioButtonGroup
from bokeh.plotting import figure

# Set up data
N = 200.
x = np.linspace(0., 4.*np.pi, N)
y_sin = np.sin(x)
y_cos = np.cos(x)
sin_src = ColumnDataSource(data=dict(x=x, y=y_sin))
cos_src =  ColumnDataSource(data=dict(x=x, y=y_cos))

# Set up plot
plot = figure(plot_height=400, plot_width=400, title="My Wave",
              tools="crosshair,pan,reset,save,wheel_zoom",
              x_range=[0., 4.*np.pi], y_range=[-2.5, 2.5])
plot.yaxis.axis_label = "Y-Axis"
plot.xaxis.axis_label = "X-Axis"

plt_sin = plot.line('x', 'y', source=sin_src, line_width=3, line_alpha=0.6, line_color="blue")
plt_cos = plot.line('x', 'y', source=cos_src, line_width=3, line_alpha=0.6, line_color="red")


# Set up widgets
text = TextInput(title="Title", value='My Wave')
offset = Slider(title="Offset", value=0.0, start=-5.0, end=5.0, step=0.1)
amplitude = Slider(title="Amplitude", value=1.0, start=-5.0, end=5.0, step=0.1)
phase = Slider(title="Phase", value=0.0, start=0.0, end=2*np.pi)
freq = Slider(title="Frequency", value=1.0, start=0.1, end=5.1, step=0.1)
types =  CheckboxGroup(labels=["Sine (blue)", "Cosine (red)"], active=[0, 1])
lang = RadioButtonGroup(labels=["ENG", "DE"], active=0)

# Set up callbacks
def update_title(attrname, old, new):
    plot.title.text = text.value

text.on_change('value', update_title)

def calc_xy(a, b, w, k):
    # Generate the new curve
    x = np.linspace(0., 4.*np.pi, N)
    y_sin = a*np.sin(k*x + w) + b
    y_cos = a*np.cos(k*x + w) + b
    
    return x, y_sin, y_cos

def update_data(attrname, old, new):

    # Get the current slider values
    a = amplitude.value
    b = offset.value
    w = phase.value
    k = freq.value

    x, y_sin, y_cos = calc_xy(a , b, w, k)

    sin_src.data = dict(x=x, y=y_sin)
    cos_src.data = dict(x=x, y=y_cos)

for w in [offset, amplitude, phase, freq]:
    w.on_change('value', update_data)

def update_type(attrname, old, new):
    
    # Get the current slider values
    a = amplitude.value
    b = offset.value
    w = phase.value
    k = freq.value
    
    x, y_sin, y_cos = calc_xy(a , b, w, k)
    
    if 0 in types.active:  
       sin_src.data = dict(x=x, y=y_sin)
    else:
       sin_src.data = dict(x=[0.], y=[0.])
       
    if 1 in types.active:  
       cos_src.data = dict(x=x, y=y_cos)
    else:
       cos_src.data = dict(x=[0.], y=[0.])
    

    
types.on_change('active', update_type)

def update_lang(attrname, old, new):
    
    if lang.active == 0:     
        tab1.title = "Input parameter"
        tab2.title = "Type"
        types.labels = ["Sine (blue)", "Cosine (red)"]
        freq.title = "Frequency"
        phase.title = "Phase"
        offset.title = "Offset"
        text.title = "Title"
        plot.yaxis.axis_label = "Y-Axis"
        plot.xaxis.axis_label = "X-Axis"
        
    if lang.active == 1:     
        tab1.title = "Eingabeparameter" ### No effect
        tab2.title = "Typ" ### No effect
        types.labels = ["Sinus (Blau)", "Cosinus (Rot)"]
        freq.title = "Frequenz"
        phase.title = "Phasenverschiebung"
        offset.title = "Y-Verschiebung"
        text.title = "Titel"
        plot.yaxis.axis_label = "Y-Achse"
        plot.xaxis.axis_label = "X-Achse"
        
lang.on_change('active', update_lang)

# Set up panels
inp_param = widgetbox(text, offset, amplitude, phase, freq)
inp_types = widgetbox(types)
inp_lang = widgetbox(lang)
l1 = layout([[inp_param]], sizing_mode='fixed')
l2 = layout([[inp_types]], sizing_mode='fixed')


# Set up panels
tab1 = Panel(child=l1, title="Input parameter")
tab2 = Panel(child=l2, title="Type")

tabs = Tabs(tabs=[ tab1, tab2 ])

# Set up layouts and add to document
#inputs = widgetbox(text, offset, amplitude, phase, freq)

curdoc().add_root(layout([lang, [tabs, plot]], sizing_mode='fixed'))
#curdoc().add_root(layout([tabs, plot], sizing_mode='fixed'))
curdoc().title = "Sliders"

```

user: pytunia
issue_7338: state: open
title: Setting areas for the activation of the hover.
body: Hi!

First of all: I am really impressed by all you have done to finally give Python some real plotting tools.

My question is the following: 
I am plotting a sort of histogram that can look like this:

```{python}
import numpy as np
atoms = np.array([ 271.183,  415.8  ,  425.948,  444.232,  444.359,  444.568, 445.236,  449.284,  449.909])
masses = np.array([  98.33,   17.23,   15.21,  208.4 ,    6.41,  117.6 ,   44.26, 19.72,  553.6 ])
```

I am plotting this with your tools as a histogram with some bar_width, while adding a hover tool:

```{python}
from bokeh.plotting import figure, show, ColumnDataSource
from bokeh.models import HoverTool

bar_width = .05
TOOLS = "crosshair pan wheel_zoom box_zoom undo redo reset box_select lasso_select save".split(' ')

plot = figure(tools=TOOLS)
bars = plot.vbar(x=atoms, top=masses, width=bar_width)
hover = HoverTool(renderers=[bars], tooltips=[("mass", "@top"), ("atom", "@x")], mode='vline')

show(plot)
```

The thing is, that now the hover is only visible when I directly point the cursor at regions [atom-barwidth, atom + bar_width].
In my application, this is not good, because when you zoom out a lot, it is difficult to aim at a particular region.

I have come up with a solution, which it to overlay invisible quads around each peak of the histogram.
These quads are centered at values of atom. 
(I have a nice code to actually automatically generate these buffers, but I skip it for simplicity).

```{python}
buffers_L = np.array([ 269.183 ,  413.8   ,  423.948 ,  444.1685,  444.2955,  444.4635, 444.902 ,  448.9715,  449.5965])

buffers_R = np.array([ 273.183 ,  417.8   ,  427.948 ,  444.2955,  444.4225,  444.6725,
        445.57  ,  449.5965,  450.2215])

source = ColumnDataSource(data={'top': masses, 'left': buffers_L, 'right': buffers_R, 'atoms': atoms})
invisible_buffers = plot.quad(left='left', right='right', top='top', bottom=0.0, alpha=0.0, source=source)

plot = figure(tools=TOOLS)
bars = plot.vbar(x=atoms, top=masses, width=bar_width)
hover = HoverTool(renderers=[invisible_buffers], tooltips=[("mass", "@top"), ("atom", "@atoms")], mode='vline')

show(plot)
```

The same effect could be achieved more easily, if the hover tool could be activated by some criterion (like the position of the cursor) and if I could control more precisely the place the hover tool points to.
For instance, an automatic criterion could be to consider the closest glyph to the cursor's position.
I would have to control the position of the hover tool, because right now it defaults to (left+right)/2, for which reason my buffers have to be symmetric around 'atoms'.
Are there any existing solutions to these questions? 
Is so, please tell me how to do it :)

I am ok with what I come up with to some extent, but 
- it might be inefficiently
- it might be not elegant 

So if you have some free time, please answer this hopefully minor *issue*.

Best wishes,

Matteo
user: MatteoLacki
issue_7330: state: open
title: bokehjs - the Minimal Complete Example need to be adapted with last 0.12.13 release
body: From the 0.12.6 release, the 
`source.trigger('change');`
has changed and has been replaced by `obj.change.emit(arg);`
See https://bokeh.pydata.org/en/latest/docs/releases/0.12.6.html#new-signaling-api

Could you update the jsfiddle code of the documentation accordingly 
https://bokeh.pydata.org/en/latest/docs/user_guide/bokehjs.html#minimal-complete-example
to help users to understand this change ?
I haven't been able to trigger the update of the plot (see https://jsfiddle.net/PBrockmann/0gf1pt04/) 
user: PBrockmann
issue_7309: state: open
title: Adding label for Span
body: I would like to see the option of adding label to vertical/horizontal "Span"s (the solid and dashed green and red lines in the attached figure)
perhaps
redspan.label = "breathing rate"

<img width="834" alt="screen shot 2017-12-11 at 10 48 19 am" src="https://user-images.githubusercontent.com/971634/33811037-4cd0b0cc-de61-11e7-86c3-083953268216.png">

user: abdelwahed
issue_7307: state: open
title: bokeh.models.GeoJSONDataSource cannot read "null" values from features in .geojson
body: I have a [geojson file in github](https://github.com/tlinnet/kmdvalg/blob/master/examples/Data/DAGI_Kommunal_1_2mio_kortforsyningen/Kommune_DAGI_1_2mio_EPSG.geojson).

Github can read it.

With[ bqplot, I can plot it:](http://nbviewer.jupyter.org/github/tlinnet/kmdvalg/blob/master/examples/test_geojson_bqplot.ipynb)

```python
from bqplot import pyplot as plt
from bqplot import (
    Map, Mercator, Orthographic, ColorScale, ColorAxis,
    AlbersUSA, topo_load, Tooltip
)
import os

sc_geo = Mercator(center=(11,56.7), scale_factor=7000)

data = os.getcwd()+os.sep+"Data"+os.sep+"DAGI_Kommunal_1_2mio_kortforsyningen"+os.sep+"Kommune_DAGI_1_2mio_EPSG.geojson"
map_mark_dk = Map(map_data=topo_load(data), scales={'projection': sc_geo})

map_style_eu = {'scales': {'projection': sc_geo}, 'colors': {'default_color': 'Grey'}}
map_mark_eu = Map(map_data=topo_load('map_data/EuropeMap.json'), **map_style_eu)

map_fig_1 = plt.figure(marks=[map_mark_eu, map_mark_dk], fig_color='deepskyblue', title='Test for Denmark Kommune map')
display(map_fig_1)
```
![screen shot 2017-12-09 at 18 01 43](https://user-images.githubusercontent.com/1208292/33797586-0e5d8308-dd0b-11e7-9d4c-29e6a8e1e204.png)



If I try with bokeh
```python
import bokeh.plotting as bplt
import bokeh.models as bm

"""# Denmark"""
data = os.getcwd()+os.sep+"Data"+os.sep+"DAGI_Kommunal_1_2mio_kortforsyningen"+os.sep+"Kommune_DAGI_1_2mio_EPSG.geojson"
# cat Kommune_DAGI_1_2mio_EPSG.geojson | grep -v "null" > Kommune_DAGI_1_2mio_EPSG_2.geojson
#data = os.getcwd()+os.sep+"Data"+os.sep+"DAGI_Kommunal_1_2mio_kortforsyningen"+os.sep+"Kommune_DAGI_1_2mio_EPSG_2.geojson"
with open(data, 'r') as f:
    gs = bm.GeoJSONDataSource(geojson=f.read())

wheel_zoom = bm.WheelZoomTool()
hover = bm.HoverTool(tooltips=[
    #("Navn", "@kommuner"),
    ("(Long, Lat)", "($x, $y)"),
    #("Dato", "@kommuner_dates"),
    #("stemme pct", "@stemme_pct %"),
    ])
hover.point_policy = "follow_mouse"
tools = [bm.PanTool(), bm.BoxZoomTool(), wheel_zoom, bm.SaveTool(), bm.ResetTool(), bm.UndoTool(), bm.RedoTool(), bm.CrosshairTool(), hover]

fig = bplt.figure(title="Test", tools=tools, x_axis_location=None, y_axis_location=None, match_aspect=True)

# Activate scrool
fig.toolbar.active_scroll = wheel_zoom
# Remove grid lines
fig.grid.grid_line_color = None

fig.patches(xs='xs', ys='ys', source=gs,
    fill_alpha=0.7, line_color="white", line_width=0.5)

"""# Show output"""
# Get output. Either to Jupyter notebook or html file 
if True:
    if kommune.check_isnotebook():
        from bokeh.io import output_notebook
        output_notebook()
        bplt.show(fig, notebook_handle=True)
    else:
        # Save to html
        filename = "bokeh.html"
        bplt.output_file(filename)
        bplt.save(fig)
        # And auto open
        import webbrowser, os
        webbrowser.open('file://' + os.path.realpath(filename))
```
![screen shot 2017-12-09 at 17 55 29](https://user-images.githubusercontent.com/1208292/33797532-47241554-dd0a-11e7-8c12-9932768af923.png)

This is because the JSON file contains features.

![screen shot 2017-12-09 at 17 57 21](https://user-images.githubusercontent.com/1208292/33797551-789d5cee-dd0a-11e7-975d-67c06de85795.png)

I can make a new geojson file without the feautures, and then it works.
```bash
cat Kommune_DAGI_1_2mio_EPSG.geojson | grep -v "null" > Kommune_DAGI_1_2mio_EPSG_2.geojson
```
![screen shot 2017-12-09 at 18 00 06](https://user-images.githubusercontent.com/1208292/33797573-d51d8cd2-dd0a-11e7-9f60-d2aa3ef7b766.png)


user: tlinnet
issue_7299: state: open
title: Expression cache and default CDSView race condition
body: Bokeh 0.12.13

Default `Expression` cache strategy in `_v_compute` is to invalidate cache on source change.
And with the introduction of default `CDSView`, each `GlyphRenderer` has a view, even when you don't specify one.

This raises two problems:
1. I think, each `GlyphRenderer` is updated two times when a data source changes:
```javascript
this.connect(this.model.data_source.change, function () {
    return this.set_data();
});
```
```javascript
this.connect(this.model.view.change, function () {
    return this.set_data();
});
```
2. Because of this, if the update caused by `CDSView` arrives first, the glyph can receive stale cache.

Here's an example that shows this behavior - you can see `TypeError: Cannot read property 'length' of undefined` when you press the button.

```python
from bokeh.core.properties import Seq, String, expr
from bokeh.io import curdoc
from bokeh.layouts import column
from bokeh.models import ColumnDataSource, Button, Expression
from bokeh.plotting import figure
from bokeh.util.compiler import CoffeeScript


class Segments(Expression):
    fields = Seq(String, help="List of column names in the data source")

    __implementation__ = CoffeeScript("""
    import {Expression} from "models/expressions/expression"
    import * as p from "core/properties"

    export class Segments extends Expression
      @define {
        fields: [ p.Array, [] ]
      }

      v_compute: (source) ->
        total_rows = source.get_length()
        result = new Array(total_rows)
        for row in [0...total_rows] by 1
          row_data = new Float64Array(@fields.length)
          for field, field_idx in @fields
            row_data[field_idx] = source.data[field][row]
          result[row] = row_data
        debugger;
        return result
    """)


source = ColumnDataSource(data=dict(start=[],
                                    end=[],
                                    y=[]))

p = figure(x_range=(0, 5), y_range=(0, 4))

p.multi_line(xs=expr(Segments(fields=['start', 'end'])),
             ys=expr(Segments(fields=['y', 'y'])),
             source=source)

b = Button(label='Break everything')


def break_everything():
    source.data = dict(start=[1, 2, 3],
                       end=[2, 3, 4],
                       y=[1, 2, 3])


b.on_click(break_everything)

curdoc().add_root(column(p, b))
```
user: p-himik
issue_7297: state: open
title: Invert Colorbar Axis
body: Have tried to invert colorbar axis by altering setting low>high.
It is useful when the user wants to see a known colorbar, for example  Blue -> Bottom, Yellow -> top and have to invert the axis values that corresponds to the colors, here high value Blue and low Value Yellow.
In the example below the problem is shown.
Inverted linear c-axis is not showing although the plot is ok (Bottom left).
Inverted log c-axis is not showing and the plot is not showing as expected (Bottom right).

![colorbar](https://user-images.githubusercontent.com/33360731/33767826-ba3a488e-dc23-11e7-89d5-5ea90cd82619.png)
Bokeh 0.12.13
```
import numpy as np

from bokeh.io import show
from bokeh.layouts import gridplot
from bokeh.models import (
    ColumnDataSource,
    ColorBar,
    LinearColorMapper,
    LogColorMapper,
)
from bokeh.palettes import Viridis3, Viridis256
from bokeh.plotting import figure

x = np.random.random(2500) * 140 - 20
y = np.random.normal(size=2500) * 2 + 5
source = ColumnDataSource(dict(x=x, y=y))
opts = dict(x='x', line_color=None, source=source)


def make_plot(mapper, title):
    mapper.low_color = 'blue'
    mapper.high_color = 'red'
    p = figure(toolbar_location=None, tools='', title=title)
    color_bar = ColorBar(color_mapper=mapper, location=(0, 0))
    p.circle(
        x='x', y='y',
        fill_color={'field': 'x', 'transform': mapper}, line_color=None,
        source=source
    )
    p.add_layout(color_bar, 'right')
    return p

p1 = make_plot(LinearColorMapper(palette=Viridis256, low=0, high=100), title='Viridis256 - Linear, low/high = blue/red')
p2 = make_plot(LogColorMapper(palette=Viridis256, low=0, high=100), title='Viridis256 - Log, low/high = blue/red')
p3 = make_plot(LinearColorMapper(palette=Viridis256, low=100, high=0), title='Viridis256 - Linear, low/high = blue/red')
p4 = make_plot(LogColorMapper(palette=Viridis256, low=100, high=0), title='Viridis256 - Log, low/high = blue/red')

show(gridplot([p1, p2, p3, p4], ncols=2, plot_width=400, plot_height=300, toolbar_location=None))
```


user: doiko
issue_7281: state: open
title: Race condition when updating data source and x_range
body: #### Software version info

- bokeh server on Debian 8 (vagrant virtual machine)
- Chrome 62.0.3202.94 (on MAC OS)
- bokeh 0.12.7, 0.12.9, 0.12.10, 0.12.11 (with `bokeh serve`)
- python 2.7
- installed python packages
```
backports-abc==0.5
bokeh==0.12.11
certifi==2017.11.5
futures==3.2.0
Jinja2==2.10
MarkupSafe==1.0
numpy==1.13.3
pandas==0.21.0
python-dateutil==2.6.1
pytz==2017.3
PyYAML==3.12
singledispatch==3.4.0.3
six==1.11.0
tornado==4.5.2
```

#### Expected behavior vs. observed behavior

- Expected behavior
I would like to update both a data source and an `x_range` of some plot in a `on_value` callback on a `TextInput`. If a data source update and an update of the `x_range` are done sequentially in the update function, I would expect the displayed `x_range` to be determined by the values to which `x_range.start` and `x_range.end` were explicitly set.

- Observed behavior
Sometimes, the displayed `x_range` behaves as if the `x_range` had not been set at all. If a sufficiently long `sleep` is performed between the data source update and the `x_range` update, the `x_range `is then displayed  in the browser as expected. **There must hence be a race condition** (I would assume on the JavaScript side).
#### Minimal code example

```
import pandas as pd

from bokeh.plotting import figure, curdoc
from bokeh.layouts import column
from bokeh.models.sources import ColumnDataSource
from bokeh.models.widgets import TextInput

from time import sleep


def dashboard():
    all_data = pd.DataFrame({'y': [1, 2, 3] * 5,
                             'x': range(15)})

    data_source = ColumnDataSource(all_data)

    selector = TextInput()

    plot = figure(plot_height=300, plot_width=1000, title='Example')
    plot.line('x', 'y', source=data_source)

    def updata_data(attr_name, old, new):
        """
        If this sleep is removed or if the sleeping time is reduced to 1 ms,
        the axis update sometimes fails.
        """
        new_data = ColumnDataSource.from_df(all_data.query("x >= {}".format(new)))
        data_source.data.update(**new_data)
        #sleep(0.2)
        plot.x_range.start = int(new) - 1.5
        plot.x_range.end = 18.5

    selector.on_change('value', updata_data)

    curdoc().add_root(column(selector, plot))


dashboard()
```

#### Stack traceback and/or browser JavaScript console output
- Bokeh server
```
(bokeh_experiments) vagrant@jessie-test:~$ bokeh serve --port 5567 --show minimal_example.py

2017-12-05 17:34:13,953 Starting Bokeh server version 0.12.11 (running on Tornado 4.5.2)
2017-12-05 17:34:13,955 Bokeh app running at: http://localhost:5567/minimal_example
2017-12-05 17:34:13,955 Starting Bokeh server with process id: 874
2017-12-05 17:34:21,149 200 GET /static/js/bokeh-gl.min.js.map (10.0.2.2) 19.66ms
2017-12-05 17:34:21,155 200 GET /static/js/bokeh-widgets.min.js.map (10.0.2.2) 32.05ms
2017-12-05 17:34:21,250 200 GET /static/js/bokeh-tables.min.js.map (10.0.2.2) 125.31ms
2017-12-05 17:34:21,458 200 GET /static/js/bokeh.min.js.map (10.0.2.2) 346.86ms
2017-12-05 17:34:23,124 200 GET /minimal_example (10.0.2.2) 77.84ms
2017-12-05 17:34:23,388 101 GET /minimal_example/ws?bokeh-protocol-version=1.0&bokeh-session-id=4e2sdAyWmhyJ29VstqB7R47el0NfDQYXsBifEOSlNIoB (10.0.2.2) 0.48ms
2017-12-05 17:34:23,388 WebSocket connection opened
2017-12-05 17:34:23,389 ServerConnection created
```

- Browser console
```
[bokeh] setting log level to: 'info'
connection.js:194 [bokeh] Websocket connection 0 is now open
widget_box.js:108 [bokeh] WidgetBox mode is fixed, but no width specified. Using default of 300.
document.js:168 [bokeh] document idle at 76 ms
embed.js:291 Bokeh items were rendered successfully
```
#### Screenshots or screencasts of the bug in action

- Desired behavior
  _Obtained by uncommenting the sleep in the above code example and typing 8 in the_ `TextInput`
<img width="1081" alt="desired_behavior" src="https://user-images.githubusercontent.com/30986601/33621110-0f8a73f4-d9ea-11e7-82d2-ff5603a4268f.png">

- Actual behavior (sometimes)
  _Obtained by commenting/removing the sleep in the above code example and typing 8 in the_ `TextInput`
<img width="1062" alt="update_x_range_fails" src="https://user-images.githubusercontent.com/30986601/33621215-57f3d888-d9ea-11e7-9dd1-88827954ebea.png">



user: smaier-by
issue_7205: state: open
title: CustomJS called after tooltip?
body: Hello,

#### Environment
- fully up-to-date Miniconda3 - bokeh 0.12.10
- HTML tested with Chrome

### Code
```
# inspired from https://bokeh.pydata.org/en/latest/docs/user_guide/interaction/callbacks.html#customjs-for-hover
from bokeh.plotting import figure, output_file, show
from bokeh.models import ColumnDataSource, HoverTool, CustomJS
import random

output_file("hover_callback.html")

count = 500
x = [random.random()*10 for _ in range(count)]
y = [3] * count
x2 = [""] * count

p = figure(plot_width=400, plot_height=400, tools="", toolbar_location=None, title='Hover over points')
cr = p.scatter(x='x', y='y', source=ColumnDataSource({ 'x': x, 'y': y, 'x2': x2 }) )

code = """
	var cdata = circle.data;
	var indices = cb_data.index['1d'].indices;
	for (i=0; i < indices.length; i++) {
		ind0 = indices[i];
		cdata.x2[ind0] = cdata.x[ind0]*cdata.x[ind0];
	}
"""

p.add_tools(HoverTool(
	tooltips =	([
				("x", "@x"),
				("y", "@y"),
				("x2", "@x2"),
			]),
	callback = CustomJS(args={'circle': cr.data_source}, code=code), 
	renderers = [cr]))

show(p)
```

#### Description of expected behavior and the observed behavior
Observed: x2 tooltip is blank 10-20% of the time when first drawn.
I'm assuming that once in a while, the tooltip is drawn before the JS code is called.

I understand that the bug tracking isn't a support platform. However, if my approach is wrong, or if there's a better way to do this, please let me know.
(Note: the x^2 operation is only a placeholder - the desired behavior is a fair bit more complex, and pre-computing that value before the graphic is render isn't an option.)
Thanks

user: KC22C2
issue_7141: state: open
title: Adding labels on categorical axis with mixed factors
body: Hello,

I am using python 2.7 with Bokeh 0.12.9. My plot is similar to the mixed factor example where I would like to label a glyph on the plot. I can draw on the plot using a tuple of strings of the two factors just fine. When I would enter either one factor string or both factor strings I get the following errors as below.

I searched the documentation, past issues (closed and open), and googled for a solution before posting here. If there is a method to achieve the labeling in the current version or 0.12.9, please add it to the documentation.

```
l = Label(x=("Q2", "apr"),y=12,text='hello world!')
p.add_layout(l)
```
https://bokeh.pydata.org/en/latest/docs/user_guide/categorical.html#mixed-factors

```
from bokeh.io import show, output_file
from bokeh.models import FactorRange,Label
from bokeh.plotting import figure

output_file("mixed.html")

factors = [
    ("Q1", "jan"), ("Q1", "feb"), ("Q1", "mar"),
    ("Q2", "apr"), ("Q2", "may"), ("Q2", "jun"),
    ("Q3", "jul"), ("Q3", "aug"), ("Q3", "sep"),
    ("Q4", "oct"), ("Q4", "nov"), ("Q4", "dec"),

]

p = figure(x_range=FactorRange(*factors), plot_height=250,
           toolbar_location=None, tools="")

x = [ 10, 12, 16, 9, 10, 8, 12, 13, 14, 14, 12, 16 ]
p.vbar(x=factors, top=x, width=0.9, alpha=0.5)

p.line(x=["Q1", "Q2", "Q3", "Q4"], y=[12, 9, 13, 14], color="red", line_width=2)

p.y_range.start = 0
p.x_range.range_padding = 0.1
p.xaxis.major_label_orientation = 1
p.xgrid.grid_line_color = None

#Add layout
l = Label(x=("Q2", "apr"),y=12,text='hello world!')
p.add_layout(l)
show(p)
```

```
python-2.7.10.amd64\lib\site-packages\bokeh\core\properties.pyc in validate(self, value)
   1096             return
   1097 
-> 1098         raise ValueError("Expected a datetime instance, got %r" % value)
   1099 
   1100 class TimeDelta(Property):

TypeError: not all arguments converted during string formatting
```

```
from bokeh.io import show, output_file
from bokeh.models import FactorRange,Label
from bokeh.plotting import figure

output_file("mixed.html")

factors = [
    ("Q1", "jan"), ("Q1", "feb"), ("Q1", "mar"),
    ("Q2", "apr"), ("Q2", "may"), ("Q2", "jun"),
    ("Q3", "jul"), ("Q3", "aug"), ("Q3", "sep"),
    ("Q4", "oct"), ("Q4", "nov"), ("Q4", "dec"),

]

p = figure(x_range=FactorRange(*factors), plot_height=250,
           toolbar_location=None, tools="")

x = [ 10, 12, 16, 9, 10, 8, 12, 13, 14, 14, 12, 16 ]
p.vbar(x=factors, top=x, width=0.9, alpha=0.5)

p.line(x=["Q1", "Q2", "Q3", "Q4"], y=[12, 9, 13, 14], color="red", line_width=2)

p.y_range.start = 0
p.x_range.range_padding = 0.1
p.xaxis.major_label_orientation = 1
p.xgrid.grid_line_color = None

#Add layout
l = Label(x='Q3',y=12,text='hello world!')
p.add_layout(l)
show(p)
```

```
python-2.7.10.amd64\lib\site-packages\bokeh\core\property\bases.pyc in prepare_value(self, obj_or_cls, name, value)
    279                     break
    280             else:
--> 281                 raise e
    282         else:
    283             value = self.transform(value)

ValueError: expected a value of type Real, got Q3 of type str
```

user: David-Herman
issue_7132: state: open
title: [BokehJS] Add change event to figure object
body: Further to use case exposed in this [stackoverflow question](https://stackoverflow.com/questions/46956603/how-to-trigger-bokehjs-plot-resize-upon-jquery-ui-resize-event),
I would like to suggest the addition of an event on figure object that could be called directly from javascript in order to trigger a "responsive" redraw/resize of the figure.

Below is an use case example where the "myMenu" div element is made resizable by jquery-ui and a custom callback to resize event is used to trigger a resizing of the figure to fill div element "myPlot".

Thanks in advance for considering this!
Cheers,
A.

```
<html>
<head>
    <link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css" type="text/css" />
    <script type="text/javascript" src="https://code.jquery.com/jquery-3.2.1.js"></script>
    <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>

    <link rel="stylesheet" href="https://cdn.pydata.org/bokeh/release/bokeh-0.12.10.css" type="text/css" />
    <script type="text/javascript" src="https://cdn.pydata.org/bokeh/release/bokeh-0.12.10.js"></script>
    <script type="text/javascript" src="https://cdn.pydata.org/bokeh/release/bokeh-api-0.12.10.js"></script>
    <!-- The order of CSS and JS imports above is important. -->

</head>
<body>
    <div style="display:table; width:100%; height:100%; margin:0px; border:0px; padding:0px;">
        <div style="display:table-row; width:100%; height:100%; margin:0px; border:0px; padding:0px;">
            <div id="myMenu" style="display:table-cell; width:25%; vertical-align:top; height:100%; margin:0px; border:0px; border-right:1px solid grey; padding:0px;">
                menu
            </div>
            <div id="myPlot" style="display:table-cell; height:100%; margin:0px; border:0px; padding:0px;"></div>
        </div>
    </div>
    <script type="text/javascript">
    // data to plot
    var source = new Bokeh.ColumnDataSource({
        data: { x: [0,1,2,3,4,5,6,7,8,9], y: [0,1,4,-2,2,5,0,2,1,1] }
    });

    // make the plot and add some tools
    var tools = "pan,crosshair,wheel_zoom,box_zoom,reset,save";

    var plot = Bokeh.Plotting.figure({
        title:"demo plot",
        tools: tools,
        toolbar_location:"above",
        sizing_mode:"stretch_both"
    });

    var scatterData = plot.line({ field: "x" }, { field: "y" }, {
        source: source,
        line_width: 2
    });

    // Show the plot, appending it to the end of the current
    // section of the document we are in.
    Bokeh.Plotting.show(plot,document.getElementById("myPlot"));

    //resizable left menu container
    $('#myMenu').resizable({
		handles:'e',
		resize:function(event,ui){plot.change.emit();}
	});
    </script>
</body>
</html>
```
user: Astrum42
issue_7117: state: open
title: DateRange Slider ToolTip getting cut off by adjacent plot
body: Hello,

I have a DateRange slider inside a widgetbox adjacent to a plot. When I slide the range slider the tool tip indicating date gets cut off. I assume a simple change in the z-index in the bokeh css would fix this. I am working with Bokeh 0.12.9

```
inputs = widgetbox(text_input1,text_input2, range_slider, text_input3)
```
```
plot_layout = layout([[[inputs,
                        plot_1,
                        plot_2,
                        plot_3],  [main_plot]]])
```
![image](https://user-images.githubusercontent.com/22305345/32109283-40c1af66-bb02-11e7-9f71-6bca6c131cf2.png)

user: David-Herman
issue_7090: state: open
title: hbar_stack: add offsets for bars
body: Feature Request:

Explained in [this SO post](https://stackoverflow.com/questions/46859431/how-do-you-organize-data-to-specify-a-range-for-an-hbar-stack-that-is-not-relati/46877789#46877789)

Summary: add functionality to render bars in hbar_stack to start at a value other than 0 
user: attaluris
issue_7070: state: open
title: Behavior of CDSView used with Line glyphs needs to be documented
body: Originally posted on the mailing list (https://groups.google.com/a/continuum.io/forum/#!msg/bokeh/GR_qCar2-nA/P6ZLnvA0AQAJ) where it was suggested that I wait for the next dev build, and if this was not resolved to report it as a bug here. I'm a little late, but the issue still exists as of yesterday's 0.12.10 release.

I'm working in Anaconda 5/Python 3.6.2 on a Windows 10 system with Bokeh 
0.12.10, looking at things in Firefox 55.0.3, 64-bit everything.

Example code for a thing that produces the expected line from points (1,1) 
to (3,3):
```
from bokeh import plotting,models

data = {'x_vals':[1,2,3,4],
        'y_vals':[1,2,3,4],
        'group':['a','b','a','c']}

data_source = models.ColumnDataSource(data=data)
current_view = models.CDSView(source=data_source, filters=[models.
IndexFilter([2,0])])
plotting.output_file("test.html")

plot = plotting.figure(plot_width=400, plot_height=400)
plot.line(x='x_vals',y='y_vals',source=data_source,view=current_view)
plotting.show(plot)
```

![bokeh_success](https://user-images.githubusercontent.com/3916078/31747046-38206722-b430-11e7-81bc-86198485ae00.png)

If I change the order of the list I'm feeding to IndexFilter it instead 
produces a plot with no line:
```
from bokeh import plotting,models

data = {'x_vals':[1,2,3,4],
        'y_vals':[1,2,3,4],
        'group':['a','b','a','c']}

data_source = models.ColumnDataSource(data=data)
current_view = models.CDSView(source=data_source, filters=[models.
IndexFilter([0,2])])
plotting.output_file("test.html")

plot = plotting.figure(plot_width=400, plot_height=400)
plot.line(x='x_vals',y='y_vals',source=data_source,view=current_view)
plotting.show(plot)
```

![bokeh_empty](https://user-images.githubusercontent.com/3916078/31747058-49dc1088-b430-11e7-84ae-9e24fd0e71ec.png)

Attempting to use GroupFilter to select points (1,1) and (3,3) also 
produces an empty plot:
```
from bokeh import plotting,models

data = {'x_vals':[1,2,3,4],
        'y_vals':[1,2,3,4],
        'group':['a','b','a','c']}

data_source = models.ColumnDataSource(data=data)
current_view = models.CDSView(source=data_source, filters=[models.
GroupFilter(column_name='group',group='a')])
plotting.output_file("test.html")

plot = plotting.figure(plot_width=400, plot_height=400)
plot.line(x='x_vals',y='y_vals',source=data_source,view=current_view)
plotting.show(plot)
```

![bokeh_empty](https://user-images.githubusercontent.com/3916078/31747058-49dc1088-b430-11e7-84ae-9e24fd0e71ec.png)

I would expect all three bits of code to produce identical plots, with a line from (1,1) to (3,3).
user: kkuntze1
issue_7024: state: open
title: ColumnDataSource.stream with callable to specify rollover policy
body: When streaming timeseries data into a ColumnDataSource I often find myself wanting to visualize a finite time window of data.  Today I think that there are two mechanisms to achieve this

1.  Provide a maximum number of elements when calling stream

        cds.stream(new_data, 10000)  # only accept 10000 elements

2.  Provide a `follow_interval` window to a `DataRange1d` object

These are useful, but my data comes in at irregular time points and I would prefer to have a fixed window in time rather than a fixed number of elements.  In my ideal world I would specify a window as follows:

    cds.stream(new_data, '10s')


user: mrocklin
issue_7006: state: open
title: GMapPlot lacks common map zoom level behaviour
body: The current zooming capabilities include the `wheel_zoom`, `zoom_in` and `zoom_out` tools outside the map rectangle. Most map makers seem to put zoom buttons inside the map rectangle, but this might be difficult using Bokeh. In any case, online maps (including Google Maps) with Mercator projection follow a well defined zooming behaviour that people nowadays are used to, with a predefined range of levels (0-19) where each level specifies the size of the shown map and much more, see http://wiki.openstreetmap.org/wiki/Zoom_levels and http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames.

The existing zooming behaviour in Bokeh 0.12.9 is more fine-grained both, for `wheel_zoom`, as well as `zoom_in` and `zoom_out`. While this might be something for most people to expect for `wheel_zoom` (associating it with a mouse wheel turning at various speeds) it most likely isn't for the buttons you get with `zoom_in` and `zoom_out`. Here, I was expecting the more usual behaviour known from classic map providers, think Google Mail, OSM, etc.

Hence, I believe it would help reduce an unexpected surprise, but, e.g., also help making map comparison tools easier etc., if this zooming behaviour would be the same as that of the widgets used by classic map providers, like Google, http://www.openstreetmap.org, or https://wego.here.com. 
user: deeplook
issue_7002: state: open
title: Add tests for CDSView (glyph rendering and hover testing)
body: Add integration tests for rendering subsets of data using glyphs other than circle and for the hover tool on filtered subsets of data. These would be tests related to [PR 6967](https://github.com/bokeh/bokeh/pull/6967).
user: clairetang6
issue_6917: state: open
title: Dynamic histogram with bokehjs
body: Hi all,
I am trying to build a dynamic histogram with BokehJs.  I am using version 0.12.7 and I tested the code on Firefox Developer Edition 56.0b10, Chrome  60.0.3112.113, and  Edge 40.15063.0.0
I initialize the histogram with a ColumnDataSource, for example in the code below I set up a histogram with 3 bars. Then I change the ColumnDataSource and update the histogram. When the new ColumnDataSource has 3 or less bars the histogram will update. However if the new ColumnDataSource has more than 3 bars only 3 bars will update, the additional bars will not show. For example in the code below I change the ColumnDataSource to have 4 bars, but the chart will show only 3 bars.


```
<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>histogram</title>
    <link rel="stylesheet" href="https://cdn.pydata.org/bokeh/release/bokeh-0.12.7.min.css" type="text/css" />
    <script type="text/javascript" src="https://cdn.pydata.org/bokeh/release/bokeh-0.12.7.min.js"></script>
    <script type="text/javascript" src="https://cdn.pydata.org/bokeh/release/bokeh-api-0.12.7.min.js"></script>
</head>

<body>

<button id="removeBar">2 bars</button>
<button id="addBar">3 bars</button>
<button id="addAnotherBar">4 bars</button>

<div id="plot"></div>

<script type="text/javascript">

    // the initial source has 3 bars
    var source = new Bokeh.ColumnDataSource({
      data: {       
          top:[2, 3, 4],
          bottom:[0,0,0],
          left:[-0.5, 0.5, 1.5],
          right:[0.5, 1.5, 2.5]
          }
    });

    var p = Bokeh.Plotting.figure({plot_width:400, plot_height:400});

    var quad = p.quad({

            top:{ field: "top" },
            bottom:{ field: 'bottom'},
            left:{ field: 'left'},
            right:{ field: 'right'},
            source: source,

            line_color:"#f0027f",
            fill_color:"#B3DE69"})

    Bokeh.Plotting.show(p, document.getElementById('plot'));


    var removeBar = document.getElementById('removeBar');

    // update source with 2 bars, this will work
    removeBar.addEventListener('click', function () {

        source.data.top=[1, 4]
        source.data.bottom=[0, 0]
        source.data.left=[-0.8, 0.2] 
        source.data.right=[0.1, 2.9]
        source.change.emit();

    });

    var addBar = document.getElementById('addBar');

    // update source with 3 bars, this will work
    addBar.addEventListener('click', function () {

        source.data.top=[1, 8, 5]
        source.data.bottom=[0, 0, 0]
        source.data.left=[-0.5, 0.5, 1.5]
        source.data.right=[0.2, 1.2, 2.5]
        source.change.emit();
    });     

    var addAnotherBar = document.getElementById('addAnotherBar');

    // update source with 4 bars, this will not work!!!
    addAnotherBar.addEventListener('click', function () {

        source.data.top = [5, 4, 6, 7]
        source.data.bottom = [0, 0, 0, 0]
        source.data.left = [-0.7, 0.5, 1.4, 2.9]
        source.data.right = [0.1, 1.3, 2.8, 3.1]
        source.change.emit();           
    }); 
</script>
</body>
</html>

```
![bokehjs](https://user-images.githubusercontent.com/7177995/30295546-dd130eca-9738-11e7-8e4f-ba8b49dac823.jpg)
user: piccicla
issue_6916: state: open
title: Feature request: Add static HTML table widget
body: Feature request: Add static HTML table widget

user: canavandl
issue_6914: state: open
title: Sliders in Tables don't match extent of table
body: Sliders on tables that don't fill their full height match the height of the figure rather than the height of the table.

![image](https://user-images.githubusercontent.com/306380/30276565-446fbca0-96d3-11e7-854b-3956272a7acd.png)

I'm not sure if this is worth fixing, but I thought I'd bring it up.
user: mrocklin
issue_6912: state: open
title: Allow user to pass column names for graphs when using data frames
body: Currently a user has to know to set node and edge columns to be indexes and then change the name of them to specific values. I think it would be more consistent with the other glyphs if they could specify the columns to use. Something like:

```
fig.graph(node_df, edge_df, 
          nodes='node-column-name', 
          edge_start='edge-start-node-column', 
          edge_end='end-end-node-column', ...)
```
or
```
fig.graph(node_df, edge_df, 
          nodes='node-column-name', 
          edge_start_node='edge-start-node-column', 
          edge_end_node='end-end-node-column', ...)
```
user: hsparra
issue_6884: state: open
title: [Feature Request] Show the errors on the user interface
body: I have asked [this](https://stackoverflow.com/questions/46042148/how-to-manage-errors-with-bokeh-library-python-javascript-and-show-them-to-t) on StackOverflow and @bryevdv told me that **"this might be reasonable to think about, but it's new development"**. So that's why I am writing this. The new feature to develop would be:

* Build the mechanism to show the errors on the user interface. Or at least capturing them in a easy way.

I think this would be useful because it is important, for the developers, that the users become conscious of those errors in order to fix them. If not maybe the are happening silently

user: chesucr
issue_6853: state: open
title: Enable notebook examples in test_examples
body: Enable notebook examples in test_examples. Currently they're all skipped.

Bokeh 0.12.7
user: canavandl
issue_6813: state: open
title: Feature request: support for x_units and y_units in imageURL
body: Dear all

**Description**
I am trying to use ImageURL to put a watermark on a interactive plot, but since ImageURL does not support x_units or y_units it is not possible to set these to "screen" and have plot image stationary to the figure when panning and zooming. I have successfully used Label to put a text string on the plot with x_units="screen" and would like to do the same with ImageURL. 

**Software versions**
bokeh: 0.12.6, python: 3.6.1, OS: Linux, browser: Chrome

**Minimal working example**
```python
#!/usr/bin/env python

from bokeh.models.glyphs import ImageURL
from bokeh.models import ColumnDataSource
from bokeh.plotting import figure, show
import numpy as np

logo_source = ColumnDataSource(dict(url=["http://bokeh.pydata.org/en/latest/_static/images/logo.png"]))
x = np.random.normal(loc=5000.0, size=400)
y = np.random.normal(loc=5000.0, size=400)

# This works
logo_image  = ImageURL(url="url", x=5000, y=5000, w=50, h=50, w_units='screen', h_units='screen', global_alpha=0.2, )
# This does not work. Not possible to set x_units='screen'
#logo_image  = ImageURL(url="url", x=5000, y=5000, w=50, h=50, w_units='screen', h_units='screen', global_alpha=0.2, x_units='screen', y_units='screen' )

p = figure(plot_width=600, plot_height=600)
p.scatter(x,y)
p.add_glyph(logo_source, logo_image)
show(p)
```

user: tjansson60
issue_6806: state: open
title: Hovering over small things is hard
body: When my plots have glyphs that are visually small it can be challenging to hover over them accurately.  Sometimes I have to hit a single pixel with my mouse, or, if zoomed out sufficiently, can't hit them at all.  

Visually Bokeh responds nicely, and small glyphs seem to always remain visible due to a minimum size border.  It's frustrating to be able to see a glyph but not hover over it.  
user: mrocklin
issue_6780: state: open
title: Add support for click policy on legend items referencing a field 
body: This case was ignored in the original implementation in PR #5349.
user: mattpap
issue_6767: state: open
title: [Feature] Fill between curve and plot frame
body: #### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
`bokeh==0.12.6`

#### Description of expected behavior and the observed behavior
I'd like to create something like a Band, which extends to infinity in one y-direction. BoxAnnotations can already do that by leaving out one of the (top, bottom) attributes.

If I provide really big numbers (`10**9`) in the upper attribute, the band will not be filled.

#### Complete, minimal, self-contained example code that reproduces the issue

```python
from bokeh.plotting import figure, output_notebook
from bokeh.models import BoxAnnotation, Band, ColumnDataSource
import numpy

output_notebook()

p = figure()
x = numpy.linspace(1, 3, 100)
p.line(x=x, y=numpy.ones(100))

# for BoxAnnotation, one can just leave out the top or bottom border
p.add_layout(BoxAnnotation(top=0.8, fill_alpha=0.1, fill_color='red'))

# a Band however needs both upper and lower to be set
# as a workaround, I use 10**6; higher values (try 10**9) lead to the band not being filled
s = ColumnDataSource(dict(base=x, 
                          lower=0.1*numpy.sin(x*5) + 1.2, 
                          upper=10**9 * numpy.ones(100)))
p.add_layout(Band(base='base', lower='lower', upper='upper', fill_alpha=0.1, fill_color='red', source=s))

show(p)
```


user: matthiasha
issue_6766: state: open
title: Using pan tool with a single dimension prevents all DataRange1d from being updated
body: Bokeh 0.12.6

```python
from bokeh.plotting import figure, curdoc
from bokeh.models import ColumnDataSource, PanTool

ds = ColumnDataSource(dict(x=[0], y=[0]))

p = figure(tools='pan')
p.select_one(PanTool).dimensions = 'width'

p.circle(x='x', y='y', source=ds)

def start_changed(attr, old, new):
    if old is None:
        return
    new = round(new)
    leftmost_x = min(round(old), min(ds.data['x']))
    if new < leftmost_x:
        data = list(range(new, leftmost_x))
        ds.stream(dict(x=data, y=data))

def end_changed(attr, old, new):
    if old is None:
        return
    new = round(new)
    rightmost_x = max(round(old), max(ds.data['x']))
    if new > rightmost_x:
        data = list(range(rightmost_x, new))
        ds.stream(dict(x=data, y=data))

p.x_range.on_change('start', start_changed)
p.x_range.on_change('end', end_changed)

curdoc().add_root(p)
```

No matter what dimension you set to a pan tool, using this tool will result in `DataRange1d.have_updated_interactively` to be set to true, which prevents it from being recalculated.



user: p-himik
issue_6662: state: open
title: Legend click policy on touchscreens
body: I have tried using a an iphone, a samsung Note 2 (android) and a touch screen laptop to click the legends on or off, but to no avail. Only clicking with a mouse has proved successful.

bokeh version: 0.12.6
python version: 2.7

sample code:
plot.legend.click_policy = "hide"

user: jhowe8
issue_6660: state: open
title: Coordinates should accept single categorical values
body: Categorical coordinates can only easily be passed in CDS columns. Trying to pass a single values results in this:

```
# nonexistent column name error
p.circle("foo", "bar", line_width=7, size=50, **options)

# property validation error (expects a number)
p.circle(value("foo"), value("bar"), line_width=7, size=50, **options)

# works but clunky
p.circle(["foo"], ["bar"], line_width=7, size=50, **options)

```


user: bryevdv
issue_6649: state: open
title: Merging toolbars across tab panels
body: When interacting with figures in different tabs ([as in this example](http://bokeh.pydata.org/en/latest/docs/user_guide/interaction/widgets.html#tab-panes)), it can be frustrating when a user selects a tool to use on the first plot and switches tabs only to have to reselect the same tool on the second plot. It would be nice to have the option to merge tools/toolbars across tab panels, similar to how [gridplot can merge tools](http://bokeh.pydata.org/en/latest/docs/user_guide/layout.html#grids-layout-for-plots).

Not sure whether it would be better for the toolbar to be located inside the tabs or outside (especially if some panels don't have figures), and it would be great if the merging still works when one of the panels has a gridplot with pre-merged tools as opposed to a figure.
 
Also, Bokeh has been fantastic to use, thanks so much!
user: shruthin
issue_6639: state: open
title: Impossible to set data for a plot that had no initial data 
body: Consider this snippet (intended for `bokeh serve`):

```python
from bokeh.layouts import row
from bokeh.plotting import figure, curdoc
from bokeh.models import ColumnDataSource, Button

s1 = ColumnDataSource(data=dict(x=range(10), y=range(10)))
f1 = figure()
f1.circle(x='x', y='y', source=s1)

s2 = ColumnDataSource(data=dict(x=[], y=[]))
f2 = figure()
f2.circle(x='x', y='y', source=s2)

b = Button(label='Update')

def update_s2():
    s2.data = dict(x=range(10), y=range(10))

b.on_click(update_s2)

curdoc().add_root(row(f1, f2, b))
```
`f1` works just fine - the ranges are computed correctly, the figure is shown without any warnings.
But since the default ranges for `f2` are `DataRange1d` and there's no initial data in `s2`, you'll see `[bokeh] could not set initial ranges` in the console. Pressing "Update" button will correctly update the second plot only if there was no interaction. If there was interaction, however, the console will be flooded with `[bokeh] could not set initial ranges` messages and pressing "Update" will do nothing.

Now, if we set ranges of `f2` to be `DataRange1d(start=0, end=1)` to get rid of the console warning about initial ranges, pressing "Update" will indeed change the plot - but the ranges will stay the same since providing explicit start and end to `DataRange1d` effectively turns it into `Range1d`.

It's pretty simple to solve this issue on a user side by using `CustomJS` callback to update `f2` ranges whenever `s2` updates. But it becomes really tiresome when there are multiple glyphs with different sources.
It also should be possible to fill empty data sources with a dummy data and make all glyphs invisible before updating the data. But it is not the best approach as well - a user will have to manually change glyphs' visibility.

At this point, I don't know any other solution and frankly, I have no idea how to fix it on the Bokeh side. Maybe implement implicit initial ranges or just somehow go around the error with no initial ranges set?

user: p-himik
issue_6575: state: open
title: Feature request: toggle between scales
body: This issue stems from [this question](https://stackoverflow.com/questions/44813611/toggle-between-linear-and-log-scale-in-bokeh?noredirect=1#comment76656677_44813611) on SO.

I noticed there's no way to toggle between a linear and a log scale with a button. The workaround now is to create a tab with the same plot in log-log scale and another in linear scale. This is a little cumbersome if I need the ability to see log-log, semilog-y, semilog-x and linear scales, because then I have to plot 4 separate plots for the combinations. It would be much better if I could have one button to toggle the x-axis and another to toggle the y-axis.

So I'd like to request that this feature be implemented. I'd help if I could, but I don't have the necessary knowledge for now.

Thank you.
user: tomchor
issue_6556: state: open
title: Add a progress bar to bokeh widgets list
body: A progress bar would be welcome. This is a very commonly used widget and its absence might constraint users to switch to another solution (e.g. ipywidgets in my case). 

user: nleclercq
issue_6536: state: open
title: How to Handle 0 values on log scales
body: ref: https://github.com/bokeh/bokeh/issues/6517
ref: https://github.com/bokeh/bokeh/issues/8061

Here is the screenshot of 1D histogram, drawn with `multi_line` and `y_axis_type="log"` in bokeh==0.12.4. Background was done using `plot.patch`

![screen shot 2017-06-26 at 12 20 45](https://user-images.githubusercontent.com/677093/27533709-3e20e5b0-5a6d-11e7-8a73-56e9f242d51d.png)

Same plot using 0.12.6:

![screen shot 2017-06-26 at 12 23 16](https://user-images.githubusercontent.com/677093/27533795-8ba9d5a8-5a6d-11e7-8ff0-a51d62c430cc.png)

Also in 0.12.6, `.patch` is used for plotting blue background

![screen shot 2017-06-26 at 12 27 27](https://user-images.githubusercontent.com/677093/27533824-a3c4524e-5a6d-11e7-9340-5bc0a195af74.png)

user: sashabaranov
issue_6517: state: open
title: Symlog axis scaling?
body: ref: https://github.com/bokeh/bokeh/issues/6536
ref: https://github.com/bokeh/bokeh/issues/8061

I'd like to add my voice to the user [here](https://stackoverflow.com/questions/41925506/symlog-scale-for-bokeh-plots) requesting the ability to scale axes similar to the "symlog" capability that matplotlib has.  That is, I need the dynamic range of log scale, but need to show negative values. 
user: timothydmorton
issue_6505: state: open
title: investigate improving rendering of Line glyph with selected and non-selected points
body: Follow-up to [PR 6491](https://github.com/bokeh/bokeh/pull/6491). A nicer way to render selected and non-selected portions of a Line glyph would be to fill in the gaps between selected and non-selected points. 

![](https://user-images.githubusercontent.com/4241244/27263449-286c4160-541e-11e7-8147-2fceed6b97b5.png)

Also, see if improvements can be made to MultiLines. 
user: clairetang6
issue_6428: state: open
title: Add layout update integration tests
body: Examples from issues #5518, #6213, #4810, #4764 and #5131 should be used as the basis.

user: mattpap
issue_6425: state: open
title: Task: bubble up selenium console output for export_png/export_svgs tests into pytest
body: Task: Bubble up selenium console output for export_png/export_svgs tests into pytest in order to assert that there's no console errors.
user: canavandl
issue_6399: state: open
title: twin axis with bounds does not respect mutual bounds
body: The example

```
howto/prevent_user_zooming_or_panning_too_far.py
```

Lets the plot be panned vertically in a way that un-aligns the twin axes. Twin axes should always be locked together. Her is before/after:

<img width="649" alt="screen shot 2017-06-06 at 21 42 36" src="https://user-images.githubusercontent.com/1078448/26860600-57044482-4b04-11e7-9f70-6f3fe6610563.png">

<img width="616" alt="screen shot 2017-06-06 at 21 42 44" src="https://user-images.githubusercontent.com/1078448/26860602-57d6c2cc-4b04-11e7-9dc1-65947354800a.png">
user: bryevdv
issue_6359: state: open
title: Legend (and other side panel element) positioning improvements
body: Currently the origin for Legends in left and right SidePanels are the top of the canvas instead of at the bottom, which may be more expected. Additionally, it's hard to align the legend with the top of the frame, which may be the ideal default behavior.

This may also be different from other annotations and cause user confusion. More investigation is needed.
user: canavandl
issue_6356: state: open
title: Colorbar tick labels truncated
body: Tick labels on the colorbar are truncated.

![bokeh_tick_label_truncation](https://cloud.githubusercontent.com/assets/13415723/26644199/d935954c-462b-11e7-9e34-c374895a39a5.png)

This only occurs for some lengths of tick label, changing the data column which controls the color of the glyphs changes the layout of the plot and the tick labels are no longer truncated.

![bokeh_tick_trunc_gif](https://cloud.githubusercontent.com/assets/13415723/26644182/cbfbfaa6-462b-11e7-8f64-e2c06d348876.gif)

Code to reproduce the top image:
```python
from bokeh.layouts import row, widgetbox
from bokeh.models import ColumnDataSource, LinearColorMapper, Select, ColorBar
from bokeh.plotting import figure, curdoc
from bokeh.palettes import Viridis256
from bokeh.sampledata import iris

df = iris.flowers

data_options = sorted(df.columns)[:-1]

p = figure(plot_height=400, plot_width=500, toolbar_location=None, id='fig')

# Select widgets
color_select = Select(title='Color', options=data_options, value=data_options[0])


def update_color(attr, old, new):
    source.data.update({'color': df[color_select.value]})
    color_mapper.high = df[color_select.value].max()
    color_mapper.low = df[color_select.value].min()


# set the on_change functions
color_select.on_change('value', update_color)


color_mapper = LinearColorMapper(low=df[color_select.value].min(),
                                 high=df[color_select.value].max(),
                                 palette=Viridis256,
                                 id='cmpr')

source = ColumnDataSource({'x': df['petal_length'],
                           'y': df['petal_width'],
                           'color': df[color_select.value]})
 

# add circle glyphs
r = p.circle('x', 'y',
             size=8,
             color={'field': 'color', 'transform': color_mapper},
             source=source, id='circles')

color_bar = ColorBar(color_mapper=color_mapper,
                     label_standoff=5,
                     location=(10, 0))

# put the pieces together
p.add_layout(color_bar, 'right')
controls = widgetbox([color_select],
                     width=200)
layout = row(controls, p)

curdoc().add_root(layout)
```

bokeh version: 0.12.5
python: 3.5

Reproduced in chrome, firefox, and the notebook.

Discussed in https://groups.google.com/a/continuum.io/forum/#!topic/bokeh/lZKC74AR6qo

user: gileshd
issue_6280: state: open
title: Undo/redo tools don't behave as expected
body: Hi, I was trying out the undo/redo tools together with box zoom and it doesn't work as expected. Specifically with this sequence of actions:

1. With box zoom, zoom in to some area of the plot
2. Click undo, which zooms back out
3. Now zoom in to some other area of the plot
4. Click undo. This goes back to the first zoomed-in region, the one after step 1. But the expected behavior is to go back to the zoomed out plot.

After this sequence of actions, cycling through undo/redo shows that there are 3 states in the history: the initial zoomed out state, the first zoomed in state, and the second zoomed in state. That is, on a zoom-in action, the "future" states from undo are not cleared properly.

I found this push_state function in this file:
https://github.com/bokeh/bokeh/blob/b4698af974c3cd10652ca899e7a93187f10be5c0/bokehjs/src/coffee/models/plots/plot_canvas.coffee#L241-L249

Specifically this line of code:

`@state.history.slice(0, @state.index + 1)`

It seems like this is a simple typo and this line should read:

`@state.history = @state.history.slice(0, @state.index + 1)`

I'm not set up to try this out at the moment, but does this seem reasonable? Or am I missing something?
user: eferreira
issue_6230: state: open
title: Task: refactor HoverTool hover/line policy and add tests
body: Task: refactor HoverTool hover/line policy and add tests

related discussion: #6215 

user: canavandl
issue_6078: state: open
title: feature request: place HoverTool box at a fixed location
body: A coworker of mine saw a scatter plot making use of the `HoverTool`. She asked, if the position of the info box could be fixed. Could I kindly make this feature request? Thanks.
user: tommycarstensen
issue_5985: state: open
title: Debouncing of JS Events
body: Events fired by the Bokeh JS-Layer run straight to the Python handlers.

So if the user scrolls the wheel a lot of requests are burdening the Python routines handling requests.

A common scheme to deal with that is debouncing. Debouncing has to happen at the issuer-side to become effective. On the responser side debouncing will fail due to the time lag between sender and recipient.

Volker
user: volkerjaenisch
issue_5983: state: open
title: Events with feedback / Simple RPC
body: This post starts as a discussion on [Reference](https://github.com/bokeh/bokeh/pull/5941).

Currently Bokeh handles JS Events as "fire and forget" when they are propagated to the Python Handlers.
This is due to the current protocol layer messaging events as changes in the model state:
1) A JS Event (e.g. change an axis by mouse operation) results in the change of a JS model property
2) The JS model change is reflected (utilizing the websockets connection) in the Python model
3) The change in the Python model results in a change Event on the Python side.
4) The Python change event triggers a python handler.

This technique is not capable to realize a JS to Python RPC (Remote Procedure Call). In particular a feedback from the Python handler to the JS Event is not possible.

The above mentioned PR will bring a new "Event" token to the Bokeh websockets protocol. But this improvement will not give us a feedback mechanism.

I would like to discuss possibilities to improve the communication layer of Bokeh. Let's discuss something like CORBA or another distributed Component-Communication protocol you prefer as an **ideal** for Bokeh to approach. Bokeh will never and should never become so complex as e.g. CORBA but we can learn from them.

Any protocol is better than to signal "something has changed in model.component.property", only. 

Volker


user: volkerjaenisch
issue_5904: state: open
title: Legend not working with GeoJSONDataSource
body: Software OS version:
Bokeh 0.12.4, Python 3.5.2, Ubuntu 16.04 64bit, Firefox 51.0.1 (64-bit)

Following the examples in [Adding Annotations](http://bokeh.pydata.org/en/latest/docs/user_guide/annotations.html) I expected the patches method to be able to auto generate legend items based on the content of the data source and on the specified color mapper even when a GeoJSONDataSource is used in place of a ColumnDataSource.
However, I found there is no way to access the geoJSON properties values from the GeoJSONDataSource object and specifying in the patches method the legend keyword as the property I want to use for legend creation only displays the property name on the plot.
See the self-contained example below, note that, since the original data is not accessible
through the GeoJSONDataSource, there is no way to count the number of elements in the data source
other than parse the JSON separately in order to create the appropriate palette for rendering.

`    
 
    import json
    from bokeh.io import show
    from bokeh.models import ( CategoricalColorMapper, GeoJSONDataSource )
    from bokeh.plotting import figure

    from bokeh.palettes import Spectral


    def create_figure():

      zon_geojson = """{
        "features": 
        [
            {
            "geometry": {
            "coordinates": [[[[0,0],[0,1],[1,1],[1,0]]]],  
            "type": "MultiPolygon"
                        },
            "properties": {
                "zone_code": "IT0204",
                "zone_name": "VdA_1"
                        },
            "type": "Feature"
            },
            {
            "geometry": {
            "coordinates": [[[[1,1],[1,2],[2,2],[2,1]]]],
            "type": "MultiPolygon"
                                },
            "properties": {
                "zone_code": "IT0205",
                "zone_name": "VdA_2"
                               },
            "type": "Feature"
            },
            {
            "geometry": {
            "coordinates": [[[[2,2],[2,3],[3,3],[3,2]]]],
            "type": "MultiPolygon"
                        },
            "properties": {
                "zone_code": "IT0206",
                "zone_name": "VdA_3"
                        },
            "type": "Feature"
            }   
     ],
    "type": "FeatureCollection"
     }""".replace('\n','')
    
     zon_source = GeoJSONDataSource(geojson=zon_geojson)
    
     p = figure()
   
     zone_codes =[x['properties']['zone_code'] for x in json.loads(zon_source.geojson)['features']] 
     
     my_palette = Spectral[len(zone_codes)]  
    
     color_mapper = \
       CategoricalColorMapper(factors=zone_codes, palette=my_palette)
    
       
     zone_plot = p.patches(xs = 'xs', ys = 'ys', 
              alpha = 0.9, 
              line_color = 'black',
              fill_color = {'field': 'zone_code', 'transform': color_mapper},
              source = zon_source,
              legend = 'zone_code',
              name = 'zones')
     return p

 
     p = create_figure() 

    show(p)
`

![immagine](https://cloud.githubusercontent.com/assets/8771314/23215893/45c2384c-f914-11e6-9a66-d27d30e6ee20.png)


user: libertil
issue_5753: state: open
title: Float value for `min_interval` disables panning when min_interval hit
body: When setting `Range1d.min_interval()`, if the value is set to a `decimal/float` value, then when zoomed fully in, then panning on that axis is disabled.  Steps for reproducing:

1. Run the plot code below
2. Zoom all the way on one axis using `WheelZoomTool`
3. Try to pan and notice that you cannot.
4. Change `min_interval` to an integer (e.g. 1), zoom all-the-way in, and panning will work fine.

**Reproduction Code:**
```python
import numpy as np
from bokeh.plotting import figure, show, output_file

N = 20
img = np.empty((N,N), dtype=np.uint32)
view = img.view(dtype=np.uint8).reshape((N, N, 4))
for i in range(N):
    for j in range(N):
        view[i, j, 0] = int(i/N*255)
        view[i, j, 1] = 158
        view[i, j, 2] = int(j/N*255)
        view[i, j, 3] = 255

# NOTICE FLOAT VALUE OF min_interval HERE
x_range = Range1d(start=0,
                  end=10,
                  min_interval=.1)

# NOTICE FLOAT VALUE OF min_interval HERE
y_range = Range1d(start=0,
                  end=10,
                  min_interval=.1)

p = figure(x_range=x_range, y_range=y_range)

# must give a vector of images
p.image_rgba(image=[img], x=0, y=0, dw=10, dh=10)

show(p)  # open a browser
```

user: brendancol
issue_5724: state: open
title: Feature Request: HoverTool tooltips stick to a point on click.
body: From my [stack overflow question](http://stackoverflow.com/questions/41640536/bokeh-how-to-make-hovertool-tooltips-stick-to-points-on-click): 

> 
> The code below comes from a [jupyter notebook](https://nbviewer.jupyter.org/github/draperjames/PythonRef/blob/master/Bokeh%20HoverTool%20Custom%20ToolTips.ipynb):
> 
>     from bokeh.io import show, output_notebook
>     from bokeh.plotting import ColumnDataSource, figure
>     from bokeh.models import HoverTool, Range1d
>     
>     output_notebook()
>     fig = figure(tools=[HoverTool(tooltips=[("html", '@html{safe}')])])
>     
>     fig.quad(left="left", top="top", bottom="bottom", right="right",
>              source=ColumnDataSource({"left": [1,3], "bottom": [1,3],
>                                       "right": [2,4], "top": [2,4],
>                                       "html":["<b>I'm bold</b>", "<span 
> 
>     style='color:red;font-size:32px;'>BIG RED TEXT</span>"]}))
>         show(fig)
> 
> I need to make the HoverTool tooltips stick to exactly where they are on a clicking the point, so if a user wanted to highlight the and copy the text in the tooltip they could. This [codepen](http://codepen.io/drapja/pen/MJyxvv) has an example of the type of behavior I would like to see. I know that this must be possible by either injecting some type of CustomJS or altering [BokehJS coffescript](https://github.com/bokeh/bokeh/blob/d2ce1971e4b984c7d30728b2378f7c959a3efd06/bokehjs/src/coffee/models/annotations/tooltip.coffee) and building BokehJS from scratch but I haven't been able to figure it out. Does anybody out there have any idea how to do this?

user: draperjames
issue_5675: state: open
title: [feature request] Serving local js/css for custom extensions
body: Hi, it would be nice for a bokeh custom extension to be able to use a local javascript/css path (e.g. relative to the directory in which the custom extension python model is defined) in `__css__` or `__javascript__`, which would get served by bokeh server. From my cursory code reading this doesn't seem to be currently possible.

It is possible with the app-based directory structure for bokeh apps, to place my custom javascript/css in `static/{js,css}/*.{js,css}` and refer to it as `{app}/static/{js,css}/*.{js,css}` in `__css__ or __javascript__` in the  custom extension. However, it is not possible then to import my custom extension for multiple apps. Of course I could also copy the js/css to each of my apps but this causes code repetition. (It is probably possible also to inject css or js from the custom extension's javascript implementation, I don't know if this is a desirable approach though.)
user: azjps
issue_5497: state: open
title: Group hover tool toolbar icons
body: Currently multiple hover tools result in multiple icons which can be visually less than ideal.

Need a way to way to distinguish diferent hover tools (by some sort of name or identifier) as well as not have so many icons as some users have use-cases involving many hovertools at the same time.
user: bryevdv
issue_5464: state: open
title: Truncate error messages to a maximum length
body: When trying to load a 15MB GeoJSON file using `GeoJSONDataSource(geojson=geojson)` I accidentally passed a list instead of a string. This triggered `ValueError expected a value of type str, got ...` which then dumped the WHOLE GeoJSON data into the notebook error output. Notebook file ballooned to 30MB from 400kB and crashed the browser because of the volume of output. Since the browser crashed on open, it was impossible to recover the notebook from within Jupyter. Was only able to recover by opening raw ipynb in SublimeText and deleting output from cell.

Bokeh should take care never to dump whole inputs, especially with GIS data which can be very large.

user: mangecoeur
issue_5402: state: open
title: feature: Updating Dates in the datatable
body: Hi - Editing datatables with dates

Before editing the date
![image](https://cloud.githubusercontent.com/assets/16763201/19662005/937b1266-99fb-11e6-9125-3da6c49b7e09.png)

during
![image](https://cloud.githubusercontent.com/assets/16763201/19662021/9f75ad1a-99fb-11e6-8588-b30fdc1dae7d.png)

Not exactly the what an end user wants to see when they change dates :)

minimally: I think the format should follow some human readable from the `DateFormatter` 

ideally: A datepicker would pop up similar to the pic below (from [handsontable](https://handsontable.com/examples.html))
![image](https://cloud.githubusercontent.com/assets/16763201/19662094/dac8377a-99fb-11e6-94c0-12d40837bc4b.png)

The code:

```
from datetime import date
from random import randint

from bokeh.io import output_file, show
from bokeh.layouts import widgetbox
from bokeh.models import ColumnDataSource
from bokeh.models.widgets import DataTable, DateFormatter, TableColumn

output_file("data_table.html")

data = dict(
        dates=[date(2014, 3, i+1) for i in range(10)],
        downloads=[randint(0, 100) for i in range(10)],
    )
source = ColumnDataSource(data)

columns = [
        TableColumn(field="dates", title="Date", formatter=DateFormatter(format='yy-mm-dd')),
        TableColumn(field="downloads", title="Downloads"),
    ]
data_table = DataTable(source=source, columns=columns, editable=True, width=400, height=280)

show(widgetbox(data_table))
```

user: gryBox
issue_5388: state: open
title: Feature:  PreText fill_color
body: It would be useful to have a PreText fill_color, both for stylistic purposes and informational reasons ... 

`PreText.fill_color = some_hex`

```
from bokeh.io import output_file, show
from bokeh.layouts import widgetbox
from bokeh.models.widgets import PreText

output_file("div.html")

pre = PreText(text="""Your text is initialized with the 'text' argument.

The remaining Paragraph arguments are 'width' and 'height'. For this example,
those values are 500 and 100 respectively.""",
width=500, height=100,  **fill_color=some_hex**)

```

user: gryBox
issue_5381: state: open
title: Trigger document resize from server-side code
body: I'm missing the possibility of resizing figures dynamically, and therefore request an exposed way of triggering a document resize (which apparently in turn updates the layout) from the server-side python code. 

As @birdsarah explains [here](http://stackoverflow.com/questions/39972162/dynamically-change-the-shape-of-bokeh-figure), this can currently be done from the client using `CustomJS`, but a server-side triggering would be cleaner.

user: alexbjorling
issue_5316: state: open
title: Replacing .y_range does not work as expected in updating Hbar Figure
body: Broken code example: https://gist.github.com/joshusre/d0a6bd37d712abc662f73c9ece9e80f0

When replacing the .y_range var with FactorRange(factors=['test_error2', 'another_error_msg2']), no update occurs on button click, but when updating .y_range.factors directly, things work as expected.  See working update func in gist.

user: joshusre
issue_5303: state: open
title: API for adding legends/colorbars etc. to side panels
body: Currently to add a Legend to a side panel you have to specify the location in coordinates e.g. `Legend(location=(0, 0))`, any of the strings won't work e.g. 'top_left', 'bottom_right'. 

It wasn't implemented at the time the feature was introduced because `top_left`, `top_right` don't make sense for side panels. Side panels expand to fill the things in them. So if they're left/right side panels then top, bottom, center (vertically) would be meaningful and if they're above/below panels then left, right, center (horizontally) are meaningful.

So we need to pick an API.

We should also probably at least think about the consistency with anything that can be put in a side panel - which is anything that gets added with the `Plot.add_layout()` method

user: birdsarah
issue_5269: state: open
title: Add support for memory usage analysis in tests
body: This is a follow up on PR #5261. The main task is to make sure that we don't introduce any memory leaks in bokehjs. Additionally, bokeh's Python API and server could be tested.

user: mattpap
issue_5267: state: open
title: Complex use of the SHIFT key
body: This has been posted in the Google groups also (https://groups.google.com/a/continuum.io/forum/#!mydiscussions/bokeh/I5X72OPYlE0) but it was suggested it gets posted here too.

I am trying to create an undo button that will revert the selected points of my graph to the previous state but I get a weird behaviour when I use the SHIFT key.

The use case that goes wrong is the following. I select some points (call it group 1). Then I select some more by pressing SHIFT (group 2). Now both group 1 and 2 are selected. Then I press the undo button and the selected points become the ones I selected originally (group 1). But then if I press the SHIFT key and select another group of points (group 3) I get a total selection of all the 3 groups. Somehow the points of group 2 are remembered and reappear whne I select group 3.

Here is a toy example that replicates this behaviour

```
from bokeh.client import push_session
from bokeh.layouts import column
from bokeh.models import BoxSelectTool, LassoSelectTool, ColumnDataSource, Circle
from bokeh.plotting import curdoc, figure
from bokeh.models.widgets import Button
import numpy as np

global previously_selected_spike_indices
global previous_tsne_source_selected

#Setup data
tsne = [np.random.random(50), np.random.random(50)]
tsne_figure_size = [500, 500]

#Scatter plot
tsne_fig_tools = "pan,wheel_zoom,box_zoom,box_select,lasso_select,tap,resize,reset,save"
tsne_figure = figure(tools=tsne_fig_tools, plot_width=tsne_figure_size[0], plot_height=tsne_figure_size[1],
                     title='T-sne', min_border=10, min_border_left=50, webgl=True)

tsne_source = ColumnDataSource({'tsne-x': tsne[0], 'tsne-y': tsne[1]})

tsne_selected_points_glyph = Circle(x='tsne-x', y='tsne-y', size=7, line_alpha=0, fill_alpha=1, fill_color='red')
tsne_nonselected_points_glyph = Circle(x='tsne-x', y='tsne-y', size=7,
                                       line_alpha=0, fill_alpha=1, fill_color='blue')

tsne_glyph_renderer = tsne_figure.add_glyph(tsne_source, tsne_nonselected_points_glyph,
                                            selection_glyph=tsne_selected_points_glyph,
                                            nonselection_glyph=tsne_nonselected_points_glyph,
                                            name='tsne_nonselected_glyph_renderer')

tsne_figure.select(BoxSelectTool).select_every_mousemove = False
tsne_figure.select(LassoSelectTool).select_every_mousemove = False

def on_tsne_data_update(attr, old, new):
    global previously_selected_spike_indices
    previously_selected_spike_indices = np.array(old['1d']['indices'])

tsne_source.on_change('selected', on_tsne_data_update)

#Undo button
undo_selected_points_button = Button(label='Undo last selection')

def on_button_undo_selection():
    global previously_selected_spike_indices
    tsne_source.data = {'tsne-x': tsne[0], 'tsne-y': tsne[1]}
    tsne_source.selected['1d']['indices'] = previously_selected_spike_indices
    old = new = tsne_source.selected
    tsne_source.trigger('selected', old, new)

undo_selected_points_button.on_click(on_button_undo_selection)


# Layout
lay = column(tsne_figure, undo_selected_points_button)

session = push_session(curdoc())
session.show(lay)  # open the document in a browser
session.loop_until_closed()
```

With the bokeh server running select a group of points, then with the SHIFT key pressed another group, then press the Undo button (the 2nd group gets removed as it should) and then with the SHIFT key pressed select a 3rd group. The 2nd group also reappears.

I have tried everything I could think of to remove the group 2 points from the ColumnDataSource's memory but I cannot even find where this information is stored. I tried to deepcopy the .selected dict but that didn't help. I tried to deepcopy the whole ColumnDataSource before the SHIFT key is pressed but it is impossible. 

user: georgedimitriadis
issue_5227: state: open
title: External resources belonging to custom models fail to load inside of Jupyter/IPython notebooks
body: The [LatexLabel example](http://bokeh.pydata.org/en/latest/docs/user_guide/extensions_gallery/latex.html) is a great resource but I'm observing problems using it with `output_notebook()`: plots are rendered with essentially no height until the browser window is resized. Removing the `LatexLabel` is all it takes to restore normality. This problem does not appear for `output_file()`.

Here is the output produced by `show()`...

![2016_09_23_18_06_15_greenshot](https://cloud.githubusercontent.com/assets/1348834/18805033/dbf0b434-81b8-11e6-957a-0f896fb1b9ce.png)

...and it's 'fixed' as soon as I change the window dimensions slightly.

![2016_09_23_18_07_07_greenshot](https://cloud.githubusercontent.com/assets/1348834/18805035/e8eb4262-81b8-11e6-8823-1a4cbf66dad6.png)

Using bokeh 0.12.2 with Anaconda 4.1.0 (Py35) on Win7 x64 + Chromium 53.0

xref #647 

user: patricktokeeffe
issue_5186: state: open
title: Colorbars change plot dimensions
body: Thanks so much for adding colorbars to Bokeh 0.12.2!  

If I take the colorbar example from http://bokeh.pydata.org/en/latest/docs/user_guide/annotations.html and comment out the colorbar, I get a nice square plot:

![image](https://cloud.githubusercontent.com/assets/1695496/18636614/17937d22-7e4f-11e6-984b-c528a0608377.png)

But if I enable the colorbar, it's all squished:

![image](https://cloud.githubusercontent.com/assets/1695496/18636639/282ac94c-7e4f-11e6-9240-a14f2f589569.png)

To me, this is very surprising behavior -- why would I want my plot to get squished when I add a colorbar?  A colorbar is something _outside_ my plot.  It seems to be putting the entire plot into a box and then adjusting the internal plot size, which makes sense for making it easier to lay out the rest of the page, but it's definitely not what I would have wanted.  And I don't see any obvious way to restore my plot to its rightful dimensions, which would appear to require finding out how big the colorbar is somehow.

The code is below; my only change was to specify the size as 200x200, but the default size has the same issue.

``` python
import numpy as np
from matplotlib.mlab import bivariate_normal

from bokeh.plotting import figure, output_file, show
from bokeh.models import LogColorMapper, LogTicker, ColorBar

output_file('color_bar.html')

N = 100
X, Y = np.mgrid[-3:3:complex(0, N), -2:2:complex(0, N)]
Z1 = bivariate_normal(X, Y, 0.1, 0.2, 1.0, 1.0) +  \
    0.1 * bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
image = Z1 * 1e6

color_mapper = LogColorMapper(palette="Viridis256", low=1, high=1e7)

plot = figure(x_range=(0,1), y_range=(0,1), height=200, width=200, toolbar_location=None)
plot.image(image=[image], color_mapper=color_mapper,
           dh=[1.0], dw=[1.0], x=[0], y=[0])

color_bar = ColorBar(color_mapper=color_mapper, ticker=LogTicker(),
                     label_standoff=12, border_line_color=None, location=(0,0))

plot.add_layout(color_bar, 'right')

show(plot)
```

user: jbednar
issue_5138: state: open
title: Creating a way to pass objects from Server to Document
body: I was considering adding to bokeh a way to pass data from a server(one started programmatically) to any bokeh applications running on that server. We already implement all of this functionality on our server, but from outside bokeh in a roundabout way that involves some monkeypatching. 
How it would work:
-user starts a server like so:

```
from bokeh.server.server import Server
from bokeh.command.util import build_single_handler_applications
#app_path_dict is a dictionary with paths to bokeh files/directories as the key, and None as the value
apps = build_single_handler_applications(app_path_dict.keys(),app_path_dict)
srv = Server(apps, some_kwargs)
```

-Then they would send objects to a document running on the server by calling something like:

```
srv.send_object_to_server('my_documents_name',the_object_im_passing,'a_key_for_the_object')
#my_documents_name is the same string that would be used to call that document from 
#autoload_server's app_path paramater. (maybe without the leading slash?)
```

-In the document code itself, you would access that object by calling something along the lines of:

```
my_object_from_server = curdoc().get_object_from_server('a_key_for_the_object')
```

-then for receiving the object in an already active session, the user define their own function for handling the new object like so:

```
@coroutine
def live_data_func():
     my_column_data_source.data = curdoc().get_object_from_server('a_key_for_the_object')
curdoc().add_live_data_callback(live_data_func) #gets called on insert data
```

To implement this, I would need to add the previously described functions to Document and Server, I would need to slightly alter session_context to pass it's instance of server_context to it's _document object, and alter Document to receive this this server_context, so that Server and Document have a common object that they can both see. 
Is this something you would like to see implemented? Any input on method names, implementation, or anything else would also be much appreciated. Thank you. 

user: adam-hubbell
issue_5101: state: open
title: autoload_static JS resource loading deficiencies / compiler caching
body: Using autoload static for examples in https://github.com/bokeh/bokeh.github.io/blob/source/content/blog/release-0-12/contents.lr , we noted two issues:
1.  In a page with two plots, if the second plot requires resources that aren't loaded by the first plot, the resources aren't ever loaded. I'd guess it's because the autoload template checks to see if Bokeh is already loaded, then returns if it's there.
2.  The autoload_static output appears to load widgets and compiler resources when unneeded.

user: canavandl
issue_5086: state: open
title: make bokeh.resources.JSResources and CSSResources crawl only models in document for external resources
body: Currently when a new ViewModel is defined (in bokeh/model.py) it's added to a `model_class_reverse_map`. When Bokeh collects the necessary resources with the JSResources and CSSResources objects (in bokeh/resources.py), it crawls the `model_class_reverse_map` dict and checks each model for a `__javascript__` and `__css__` attribute.

The issue is that there are use cases where a user will create custom models that aren't included in the document. Those models' resources will be included in the output element regardless. Apart from slower page loads, it's possible to have conflicting resources.

The solution is likely to make sure that the model is actually in the document.

user: canavandl
issue_5064: state: open
title: Enhance BokehJS error reporting 
body: Following on previous work, some additional possible improvements:
- larger target to "close"
- collapsible stack trace detail
- friendlier and more informative wording. 

This would be my ideal:

```
Bokeh has encountered the following error:                                     close [x]

xr is null

<click to expand full stack trace>
```

user: bryevdv
issue_5057: state: open
title: WebGL Patches
body: Request to support patches with webgl.

@almarklein and I discussed this and the only challenge was that we needed an algorithm to turn patches into triangles. I have found: https://github.com/mapbox/earcut "The fastest and smallest JavaScript polygon triangulation library. 2.5KB gzipped" which seems to do exactly what we need and comes from the awesome mapbox.

Currently there are inefficiencies around _build_discontinuous_objects being called many times (called on every render) which would likely need to be solved to see the impacts of webgl (ping @bryevdv and #4967).

user: birdsarah
issue_4967: state: open
title: _map_data being called too often
body: Following on from discussion on #4953 

We compute sxss and syss on every render. Could this just be done at _map_data stage.

user: birdsarah
labels: grant: CZI R5: Funded by CZI Round 5 grant
issue_4951: state: open
title: ModelExtensions - all resources from a whole module are loaded
body: I setup a series of ModelExtensions with a base with the implementation and a bunch of subclasses for different skins.

``` python
class IonRangeSliderFlat(IonRangeSliderBase):
    __css__ = [
        "/css/ion.rangeSlider.css",
        "/css/ion.rangeSlider.skinFlat.css",
    ]


class IonRangeSliderHTML5(IonRangeSliderBase):
    __css__ = [
        "/css/ion.rangeSlider.css",
        "/css/ion.rangeSlider.skinHTML5.css",
    ]


class IonRangeSliderModern(IonRangeSliderBase):
    __css__ = [
        "/css/ion.rangeSlider.css",
        "/css/ion.rangeSlider.skinModern.css",
    ]
```

When I used one of them:

``` python
from ion_slider import IonRangeSliderFlat
s = IonRangeSliderFlat(..)
...
```

All the CSS from all the different subclasses were stuck into my html.

user: birdsarah
issue_4935: state: open
title: GeoJSONDataSource renders multiples of discontinuous items
body: The following:

``` python
from bokeh.io import output_notebook, show
output_notebook()

import json
from bokeh.models import GeoJSONDataSource, HoverTool
from bokeh.plotting import figure
from shapely import affinity
from shapely.geometry import mapping, shape

def get_us_coords():
    with open('us_states_simplified_albers.geojson') as us_states_file:
        geojson = json.loads(us_states_file.read())
        for i, f in enumerate(geojson['features']):
            if f['properties']['NAME'] == 'Alaska':
                geom = affinity.translate(shape(f['geometry']), xoff=1.2e6, yoff=-4.8e6)
                geom = affinity.scale(geom, .45, .45)
                geom = affinity.rotate(geom, 30)
                f['geometry'] = mapping(geom)
            elif f['properties']['NAME'] == 'Hawaii':
                geom = affinity.translate(shape(f['geometry']), xoff=4.75e6, yoff=-1.1e6)
                geom = affinity.scale(geom, 1.15, 1.15)
                geom = affinity.rotate(geom, 30)
                f['geometry'] = mapping(geom)
    return GeoJSONDataSource(geojson=json.dumps(geojson))

geojson = get_us_coords()

x_range = (-3e6, 3e6)
y_range = (-2e6, 1.5e6)

ar = (x_range[1] - x_range[0]) / (y_range[1] - y_range[0])
width = 800

fig = figure(
    plot_width=width, plot_height=int(width / ar),
    x_range=x_range, y_range=y_range,
    x_axis_type=None, y_axis_type=None, min_border=0,
    tools='tap', toolbar_location=None, outline_line_color=None
)
fig.grid.visible = False
fig.add_tools(HoverTool(tooltips='@NAME'))
fig.patches(xs='xs', ys='ys', line_color='#333333', line_width=1, fill_alpha=0.5, source=geojson)
show(fig)
```

Produces:
<img width="823" alt="screen shot 2016-08-08 at 5 21 30 pm" src="https://cloud.githubusercontent.com/assets/1796208/17500711/c0cb4408-5d8c-11e6-8cac-f67284a58033.png">

The states that have discontinuities in them have been rendered more times than those without. The number of times they are rendered correlates to the number of discontinuities they have which is why Hawaii is the darkest.

[data.zip](https://github.com/bokeh/bokeh/files/407973/data.zip)

user: birdsarah
issue_4653: state: open
title: All widget documents with  multi-column layouts don't initialize properly in non fixed
body: If sizing_mode is `scale_width` or `stretch_both` the following layout doesn't initialize correctly.

``` python
l = layout([
    [buttons, groups],
    [markups, inputs],
], sizing_mode=sizing_mode)
```

If you add a plot it's fine. In fixed mode it's fine. This seems like a corner case - we're a plotting library not a widgets library. Although it would, of course, be nice to fix it.

Code for testing:

``` python
from bokeh.models.widgets import (
    # buttons
    Button, Dropdown, Toggle,
    # icons
    Icon,
    # groups
    CheckboxButtonGroup, CheckboxGroup, RadioButtonGroup, RadioGroup,
    # inputs
    AutocompleteInput, DatePicker, DateRangeSlider, InputWidget, MultiSelect, Select, Slider, TextInput,
    # markups
    Div, Paragraph, PreText,
    # dialog
    Dialog,
)
# We're not demo-ing Tabs or DataTable, as they are available in other demos
from bokeh.layouts import widgetbox, layout, column
from bokeh.io import show
from bokeh.plotting import figure

sizing_mode = 'stretch_both'

# Buttons
button = Button(label="Button (with icon)", icon=Icon(icon_name="check"))
toggle = Toggle()
dropdown = Dropdown(menu=[("Item 1", "item_1_value"), ("Item 2", "item_2_value")], default_value="item_1_value")
buttons = widgetbox(Paragraph(text="The bokeh buttons"), button, toggle, dropdown, sizing_mode=sizing_mode)

# Dialog
# dialog = Dialog(closable=True, content="This is a dialog", title="Dialog", visible=True)

# Groups
checkbox_group = CheckboxGroup(labels=["Option 1", "Option 2", "Option 3"], active=[0, 1])
radio_group = RadioGroup(labels=["Option 1", "Option 2", "Option 3"], active=0)
checkbox_button_group = CheckboxButtonGroup(labels=["Option 1", "Option 2", "Option 3"], active=[0, 1])
radio_button_group = RadioButtonGroup(labels=["Option 1", "Option 2", "Option 3"], active=0)
groups = widgetbox(Paragraph(text="The bokeh groups"), checkbox_group, radio_group, checkbox_button_group, radio_button_group, sizing_mode=sizing_mode)

# Markups
div = Div(text="Some custom <strong>bold</strong> text")
paragraph = Paragraph(text="this is a paragraph")
pretext_1 = PreText(text="This is pre. I could put a bunch of code in here perhaps\nOr maybe something else")
pretext_2 = PreText(text="This is pre")
pretext_3 = PreText(text="This is pre it's a bit longer and i'm going to constrict it.", width=150)
markups = widgetbox(Paragraph(text="The bokeh markups"), div, paragraph, pretext_1, pretext_2, pretext_3, sizing_mode=sizing_mode)

# Inputs
#daterangeslider = DateRangeSlider()  # Known broken
slider = Slider(title="Slider", start=0, end=10, width=100)
multiselect = MultiSelect(options=['1', '2', '3'])
select = Select(options=['1', '2', '3'])
textinput = TextInput()
autocomplete = AutocompleteInput(completions=['one', 'two', 'three'])
#datepicker = DatePicker()
inputs = widgetbox(Paragraph(text="The bokeh inputs"), slider, multiselect, select, textinput, autocomplete, sizing_mode=sizing_mode)

plot = figure(sizing_mode=sizing_mode)
plot.circle(x=[1, 2], y=[2, 3])

l = layout([
    [buttons, groups],
    [markups, inputs],
    [plot],
], sizing_mode=sizing_mode)

show(l)

```

user: birdsarah
issue_4470: state: open
title: Add parallel coordinates plot
body: Despite this specific kind of plot is already mentioned in [#372], I would like to upvote the parallel coordinates plot (https://en.wikipedia.org/wiki/Parallel_coordinates) by adding this  request.

user: PJussel
issue_4134: state: open
title: Document the JavaScript / Typescript API
body: - [ ] provide examples that open in CodePen pens
- [ ] give guidance for interacting with models as it applies to JS callbacks

dev part
- [ ] explain how to maintain (install) external typings

user: bryevdv
issue_3821: state: open
title: Arbitrary Frame Locations on Canvas
user: hsparra
issue_3804: state: open
title: support for multiple tap tools
body: I want to register two different tap callbacks against two different glyphs in a plot.  Unless I'm mistaken, I cannot have two tap tools on at the same time - I only have the option to manually toggle between the two.  Since each tap tool is registered against a specific glyph, it seems like this behavior should be possible.

user: hafen
issue_3708: state: open
title: Supporting GeoJSON
body: Pulling out a conversation started on #3399 (Patches with hole).
#### Background

We want to support drawing an arbitrary polygon with cutouts. This is particularly useful in geo contexts - e.g. countries have holes in them - for lakes, Lesotho lives entirely inside South Africa, etc.
#### Patch & Patches

`Patch` and `Patches` already support arbitrary polygons, but not cutouts.
- `Patch` accepts a list of x and y coordinates: `x = [1, 2, 3], y=[2, 3, 4]`
- `Patches` accepts a list of lists of x and y coordinates: `xs = [[1, 2, 3]], ys=[[2, 3, 4]]`

(The above would draw exactly the same, the difference is that the ColumnDataSource for `Patch` has 3 rows, but for `Patches` it has 1.)

In addition, in both cases, these coordinates can be separated by `NaN`s which makes discontinuous patches - that is Bokeh sees them as the same object (they get the same styling and are hit tested as the same object) but they are visually distinct - Hawaii and the rest of the United States:
- `Patch`: `x = [1,2,3,4, nan, 5,6,7], y = [1, 2, 3, 4, nan, 5, 6, 7]`
- `Patches`: `xs = [[1, 2, 3, 4], [1,2,3,4, nan, 5,6,7], ...], ys = [[1, 2, 3, 4], [1,2,3,4, nan, 5,6,7], ...]`
#### Polygon and MultiPolygon

GeoJSON (http://geojson.org/) is a well-known and widely-used specification for describing geographic structures. It describes a `Polygon` and a `MultiPolygon` as follows
###### Polygon - http://geojson.org/geojson-spec.html#id4

Coordinates of a Polygon are an array of LinearRing coordinate arrays. The first element in the array represents the exterior ring. Any subsequent elements represent interior rings (or holes).

``` json
No holes:

{ "type": "Polygon",
    "coordinates": [
      [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0] ]
      ]
   }

With holes:

{ "type": "Polygon",
    "coordinates": [
      [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0] ],
      [ [100.2, 0.2], [100.8, 0.2], [100.8, 0.8], [100.2, 0.8], [100.2, 0.2] ]
      ]
   }
```
###### MultiPolygon - http://geojson.org/geojson-spec.html#id7

Coordinates of a MultiPolygon are an array of Polygon coordinate arrays:

``` json
{ "type": "MultiPolygon",
    "coordinates": [
      [[[102.0, 2.0], [103.0, 2.0], [103.0, 3.0], [102.0, 3.0], [102.0, 2.0]]],
      [[[100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0]],
       [[100.2, 0.2], [100.8, 0.2], [100.8, 0.8], [100.2, 0.8], [100.2, 0.2]]]
   ]
}
```

---
#### Unification

The motivation of bringing the Patches (and Patch?) inline with GeoJSON is, as put bluntly by @bryevdv:

>  My impression is that pretty much everyone would rather use the popular standard format instead of the non-standard one we invented anyway? We can certainly have public functions that help create GeoJSON out of other formats. 

See https://github.com/bokeh/bokeh/pull/3399#issuecomment-173783522 onwards for a full discussion.

Some challenges:
- GeoJSON always contains pairs of co-ordinates - we keep coordinates in seperate columns
- As a result of the pairs of co-ordinates, GeoJSON is naturally one level more nested that Bokeh's representation
- The discontinuous `Patch` or `Patches` in Bokeh correspond exactly to GeoJSON's `MultiPolygon` in terms of what we are trying to represent. However, Bokeh separates the Polygons of a MultiPolygon with a `NaN`, GeoJSON separates them as a list of lists.
- The numbers that power `Patch` can be thought of as a column of single values from a table. As such there will never be a way to represent a MultiPolygon without using a seperator such as `NaN`. 
- (Similarly if you wanted `Patch` to hold a polygon with a hole you would need another seperator - no-one is proposing this, just mentioning for completeness)
- With my head in the context of this issue, I would propose removing support for `NaN` in Patch so that Patch was always just a single Polygon.
- The data structure for Patches allows for GeoJSON style representation of all GeoJSON objects - `Polygon`, `Polygon with hole`, `Multi-Polygon`, `Multi-Polygons with holes`

In the simplest change, the data source for a MultiPolygon GeoJSON style for Patches if would change from this:

``` python
# Note this would show four distinct shapes, two belong to one "row", and two to the other
{ "xs": [
      [102.0, 103.0, 103.0, 102.0, 102.0, NaN, 100.2, 100.8, 100.8, 100.2, 100.2], 
      [100.0, 101.0, 101.0, 100.0, 100.0, NaN, 100.2, 100.8, 100.8, 100.2, 100.2]
   ]
  "ys": [
      [2.0, 2.0, 3.0, 3.0, 2.0, NaN, 0.2, 0.2, 0.8, 0.8, 0.2],
      [1.0, 1.0, 0.0, 0.0, 1.0, NaN, 0.2, 0.2, 0.8, 0.8, 0.2]
   ]
}
```

to this:

``` python
# warning: not my proposal
{ "xs": [
      [[102.0, 103.0, 103.0, 102.0, 102.0], [100.2, 100.8, 100.8, 100.2, 100.2]], 
      [[100.0, 101.0, 101.0, 100.0, 100.0], [100.2, 100.8, 100.8, 100.2, 100.2]]
   ]
  "ys": [
      [[2.0, 2.0, 3.0, 3.0, 2.0], [0.2, 0.2, 0.8, 0.8, 0.2]],
      [[1.0, 1.0, 0.0, 0.0, 1.0], [0.2, 0.2, 0.8, 0.8, 0.2]]
   ]
}
```

The change is pretty clear, what was separated by `NaN`'s is now in lists. However, we need to add another level of nesting like this:

``` python
{ "xs": [
      [[[102.0, 103.0, 103.0, 102.0, 102.0]], [[100.2, 100.8, 100.8, 100.2, 100.2]]], 
      [[[100.0, 101.0, 101.0, 100.0, 100.0]], [[100.2, 100.8, 100.8, 100.2, 100.2]]]
   ]
  "ys": [
      [[[2.0, 2.0, 3.0, 3.0, 2.0]], [[0.2, 0.2, 0.8, 0.8, 0.2]]],
      [[[1.0, 1.0, 0.0, 0.0, 1.0]], [[0.2, 0.2, 0.8, 0.8, 0.2]]]
   ]
}
```

Although this seems like a lot, it allows us to then support Patches with a hole, in the same manner that GeoJSON does. In the following example, a triangular cut out is taken from the second Polygon of the second row:

``` python
{ "xs": [
      [[[102.0, 103.0, 103.0, 102.0, 102.0]], [[100.2, 100.8, 100.8, 100.2, 100.2]]], 
      [[[100.0, 101.0, 101.0, 100.0, 100.0]], [[100.2, 100.8, 100.8, 100.2, 100.2], [100.4, 100.6, 100.4]]]
   ]
  "ys": [
      [[[2.0, 2.0, 3.0, 3.0, 2.0]], [[0.2, 0.2, 0.8, 0.8, 0.2]]],
      [[[1.0, 1.0, 0.0, 0.0, 1.0]], [[0.2, 0.2, 0.8, 0.8, 0.2],  [0.3, 0.4 0.6]]]
   ]
}
```

---
## Conclusion

In writing all this out. I am thinking it would be be better to make two new glyphs `Polygon` and `MultiPolygon` that adhere to the GeoJSON standard, including supporting holes. There would be two choices:
- they still follow the x and y as separate and then we deprecate Patch and Patches over time
- they accept x, y pairs of coordinates (or x, y, z if we're going with GeoJSON although Bokeh doesn't have 3d capabilities) and leave Patch and Patches working as they do now.

If we do that, that would only leave us Line and MultiLine. GeoJSON calls their equivalent LineString and MultiLineString. 

Similar to above I would propose adding two new glyphs that just follow the GeoJSON spec.

Overall, I think my preference would be adding `Polygon`, `MultiPolygon`, `Line`, `MultiLine`, that accept triplets of coordinates and only work with GeoJSON data source, and we leave `Patch`, `Patches`, `Line`, `MultiLine` alone. 

In that case, I think the remaining confusion is the naming of `MultiLine` (because Multi has a meaning associated with GeoJSON) and it may be better to deprecate `MultiLine` and rename it to `Lines` a la `Patches`.

user: birdsarah
issue_3598: state: open
title: Bokeh "expand to fullscreen" for demonstration purposes.
body: Do you think it would be good to have a feature that allows a bokeh plot to explode out to fill the screen? 

An example of where this sort of thing works is in the diagramming tool "lucid charts", which has a feature like this and it is _very_ helpful when demonstrating work to people.

I think this could be a widget that you can click on like any of the other tools.

user: nfaggian
issue_3519: state: open
title: Need indication on disconnect/session timeout
body: Right now things just stop working with only a console message. 

user: bryevdv
issue_3393: state: open
title: Add busy/working spinner 
body: Should be able to make use of WORKING protocol message (send OK to indicate completed? or maybe new message type?)

@havocp can you point @canavandl at the partial code you had for a busy spinner in the previous bokeh develop mode? 

user: bryevdv
issue_3000: state: open
title: Crosshair tool to show the value on both axis
body: Appreciate any help on this. I think Bokeh's strength against other viz lib is that it allows you to inspect the elements of the chart.

For the CrosshairTool, I am wondering if there is a way for me to implement a feature when I hover over a particular point in the plot, a number / date value can be shown in the places where the crosshair crosses the x and y axis.

thanks!

user: Paul-Yuchao-Dong
issue_2996: state: open
title: Minor ticks for datetime x-axis
body: I am experimenting with time plots such as this [example](http://bokeh.pydata.org/en/0.10.0/docs/user_guide/plotting.html#datetime-axes) from `bokeh`. Is it possible to create minor ticks for the x-axis? I tried all the different options inside `p.xaxis.minor` but none seemed useful.

Here's the code from example:

```
import pandas as pd
from bokeh.plotting import figure, output_file, show

AAPL = pd.read_csv(
        "http://ichart.yahoo.com/table.csv?s=AAPL&a=0&b=1&c=2000&d=0&e=1&f=2010",
        parse_dates=['Date']
    )

output_file("datetime.html")

# create a new plot with a datetime axis type
p = figure(width=800, height=250, x_axis_type="datetime")

p.line(AAPL['Date'], AAPL['Close'], color='navy', alpha=0.5)

show(p)
```

And the image:

[![enter image description here](http://i.stack.imgur.com/WfaQq.png)](http://i.stack.imgur.com/WfaQq.png)

From what it seems it's automatically turned off, and the `x-axis` updates properly as you zoom in, but it would be great to include minor ticks for visualization purpose.

user: jelmelk
issue_2981: state: open
title: Force-directed text annotations
body: Have already mentioned this to @canavandl, would be great if text labels could have an option to be force-directed so they don't sit on top of one another.

Obviously, this should all be user configurable, but it would be good to provide some helpful default layouts. This may be something that the layout solver could do for us?

For example, the charts here: http://blogs.worldbank.org/opendata/global-state-gender-7-charts

<img width="686" alt="screen shot 2015-10-12 at 6 38 56 pm" src="https://cloud.githubusercontent.com/assets/1796208/10441616/8db6a652-7110-11e5-9cf0-0bac8706af0e.png">

user: birdsarah
issue_2903: state: open
title: CheckboxEditor in DataTable does not display value without focus
body: Using the CheckboxEditor in a DataTable the value of the field (checked or unchecked) does not display unless the cell has focus (by double-clicking it).
- In Chome, the checkbox appears off center and is not toggleable.
- In Firefox, the checkbox appears centered and is toggleable.
- In both browsers the value is only observable by double-clicking the cell.

Firefox:
![screen shot 2015-09-23 at 6 32 30 pm](https://cloud.githubusercontent.com/assets/1168692/10063376/af0de8cc-6221-11e5-83db-01fac6ba2a32.png)

Chrome:
![screen shot 2015-09-23 at 6 35 40 pm](https://cloud.githubusercontent.com/assets/1168692/10063393/f0cbd36e-6221-11e5-9212-acd868be650b.png)

I expected to see a checkbox in each cell, whether or not it was selected.

Here's code that shows the problem.  Runs in Jupyter notebook.

```
from bokeh.plotting import output_notebook
output_notebook()

from bokeh.io import vplot
from bokeh.models import ColumnDataSource, DataRange1d, Plot, LinearAxis, Grid, Circle, HoverTool, BoxSelectTool
from bokeh.models.widgets import (DataTable, TableColumn, 
                                  StringFormatter, NumberFormatter, BooleanFormatter,
                                  StringEditor,  IntEditor, NumberEditor, SelectEditor, CheckboxEditor)
from bokeh.plotting import show
from bokeh.sampledata.autompg2 import autompg2 as mpg

# add a boolean column
mpg['efficient'] = mpg['hwy'] > mpg['hwy'].mean()

source = ColumnDataSource(mpg)

manufacturers = sorted(mpg["manufacturer"].unique())
models = sorted(mpg["model"].unique())
transmissions = sorted(mpg["trans"].unique())
drives = sorted(mpg["drv"].unique())
classes = sorted(mpg["class"].unique())

columns = [
    TableColumn(field="manufacturer", title="Manufacturer", editor=SelectEditor(options=manufacturers), formatter=StringFormatter(font_style="bold")),
    TableColumn(field="model",        title="Model",        editor=StringEditor(completions=models)),
    TableColumn(field="displ",        title="Displacement", editor=NumberEditor(step=0.1),              formatter=NumberFormatter(format="0.0")),
    TableColumn(field="year",         title="Year",         editor=IntEditor()),
    TableColumn(field="cyl",          title="Cylinders",    editor=IntEditor()),
    TableColumn(field="trans",        title="Transmission", editor=SelectEditor(options=transmissions)),
    TableColumn(field="drv",          title="Drive",        editor=SelectEditor(options=drives)),
    TableColumn(field="class",        title="Class",        editor=SelectEditor(options=classes)),
    TableColumn(field="cty",          title="City MPG",     editor=IntEditor()),
    TableColumn(field="hwy",          title="Highway MPG",  editor=IntEditor()),
    TableColumn(field="efficient",    title="Efficient",    editor=CheckboxEditor(), formatter=BooleanFormatter(icon='check')),
]
data_table = DataTable(source=source, columns=columns, editable=True)

plot = Plot(title=None, x_range= DataRange1d(), y_range=DataRange1d(), plot_width=1000, plot_height=300)

# Set up x & y axis
plot.add_layout(LinearAxis(), 'below')
yaxis = LinearAxis()
plot.add_layout(yaxis, 'left')
plot.add_layout(Grid(dimension=1, ticker=yaxis.ticker))

# Add Glyphs
cty_glyph = Circle(x="index", y="cty", fill_color="#396285", size=8, fill_alpha=0.5, line_alpha=0.5)
hwy_glyph = Circle(x="index", y="hwy", fill_color="#CE603D", size=8, fill_alpha=0.5, line_alpha=0.5)
cty = plot.add_glyph(source, cty_glyph)
hwy = plot.add_glyph(source, hwy_glyph)

# Add the tools
tooltips = [
    ("Manufacturer", "@manufacturer"),
    ("Model", "@model"),
    ("Displacement", "@displ"),
    ("Year", "@year"),
    ("Cylinders", "@cyl"),
    ("Transmission", "@trans"),
    ("Drive", "@drv"),
    ("Class", "@class"),
]
cty_hover_tool = HoverTool(renderers=[cty], tooltips=tooltips + [("City MPG", "@cty")])
hwy_hover_tool = HoverTool(renderers=[hwy], tooltips=tooltips + [("Highway MPG", "@hwy")])
select_tool = BoxSelectTool(renderers=[cty, hwy], dimensions=['width'])
plot.add_tools(cty_hover_tool, hwy_hover_tool, select_tool)

layout = vplot(plot, data_table)
show(layout)
```

user: dennisobrien
issue_2839: state: open
title: Enable remote source for AutocompleteInput and StringEditor
body: Enable Autocomplete widgets to use a remote source for completion, like when creating an AJAX-style autocomplete on a plain html input (https://jqueryui.com/autocomplete/).

As far as I am aware, this applies to AutocompleteInput and StringEditor for DataTables

user: PythonicPrime
issue_2603: state: open
title: Allow size to vary in legend glyphs
body: When size is being varied in a plot according to some variable in the data, it is useful to make a legend that ties the size of the glyph to some numeric value.  Currently size in a legend is fixed.  Here's an example:

![rplot](https://cloud.githubusercontent.com/assets/1275592/8843362/95990f70-30d5-11e5-9862-6afb151a3dfc.png)

In this case in bokeh, I would just create some dummy glyphs with varying sizes and use those for the legend.

user: hafen
issue_1135: state: open
title: datetime axis type not handling timezone correctly
body: When trying to plot a pandas dataframe with a datetime index which has a tz, the plot does not adapt and put the x axis tick in UTC

```
import pandas as pd
import random
from bokeh.plotting import hold, figure, ColumnDataSource, scatter, curplot, show, line, output_file
from collections import OrderedDict   
from bokeh.objects import HoverTool

dates = pd.date_range('2014/09/01 00:00', freq='H', periods=24, tz='Europe/Berlin')
data = pd.DataFrame(rand(48).reshape(24,2), dates, columns=['a', 'b'])

data['index'] = data.index       
data['index_str'] = [i.strftime('%Y/%m/%d %H:%M') for i in data.index]


hold()
output_file("test.html")
figure(x_axis_type="datetime", tools='hover') 

r = lambda: random.randint(20,235) 
for  col in data.columns[:-2]:
    c_data = data[['index', 'index_str', col]]
    c_data.columns = ['index', 'index_str', 'value']            
    source = ColumnDataSource(dict(c_data))    

    color = ('#%02X%02X%02X' % (r(),r(),r()))                      
    scatter('index', 'value', source=source, legend=col, color=color)
    line('index', 'value', source=source, legend=col, color=color)

# Built the hover      
hover = [t for t in curplot().tools if isinstance(t, HoverTool)][0]        
hover.tooltips = OrderedDict([
    ('index', '@index_str'),
    ('value', '@value')])


show()        

```

```
Bokeh version: 0.5.2
Python version: 2.7.6-CPython
Platform: Windows-7-6.1.7601-SP1
```

user: gbakalian
issue_601: state: open
title: Add support for click events on categorical axes
body: This should be supported on client and server side. As an example consider two plots where one has years on an axis and clicking that year updates another plot with data from only that year. Obviously this can be solved with widgets, but the behavior proposed here can be more intuitive for users, at least in some contexts.

*(deleted unrelated discussion about categorical zooming below)*
user: mattpap
issue_182: state: open
title: measurement tools
body: Would be nice to have tools that can measure and report:
- distances
- angles
- color / intensity
- count (can be tricky... need a quantization setting)
- area in pixels
- current plot bounds

For all measures, if multiple items are counted, then need to be able to show ratios between the quantities

user: bryevdv
issue_128: state: open
title: Add a python way to schedule purely js animations
body: Add a python construct that scripts running animations that are purely done in javascript.  This way static examples can be built that have animations.

maybe something like this

```
N = 80
x = np.linspace(0, 4*np.pi, N)

y = np.sin(x)
y2 = np.tan(x)
y3 = np.cos(x)

y_possibilities = [y,y2,y3]

l = line(x,y, color="#0000FF", tools="pan,zoom,resize")

animate(animate.select(y_posibilities), delay="3s", line)

```

that is a very very rough syntax.  But something like that should produce a bokeh object that every 3 seconds switches out the y data range to one of the other possibilities.  

user: paddymul