Repository: bokeh/bokeh

Issue:
ColorMapper high, low and nan_color do not accept RGB(A) tuples
When specifying the `high_color`, `low_color` or `nan_color` as an RGB(A) tuple the JS code fails with the following exception:

```
Uncaught TypeError: t.toLowerCase is not a function
```

Here is a minimal example:

``` python
import numpy as np
from bokeh.plotting import figure, output_notebook, show
from bokeh.models import LinearColorMapper, ColumnDataSource
from bokeh.palettes import viridis

output_notebook()

p = figure(plot_width=400, plot_height=400, x_range=(0, 1), y_range=(0, 1))

array = np.random.rand(100,3)
array[20:30, 2] = np.NaN

source= ColumnDataSource(data=dict(x=array[:, 0], y=array[:, 1], z=array[:, 2]))
cmapper = LinearColorMapper(viridis(256), low=0.25, high=0.75, nan_color='#333333',
                            low_color='red', high_color=(255, 1, 1))
p.scatter(x='x', y='y', color={'field': 'z', 'transform': cmapper}, source=source)
show(p)
```

Version Information:

Bokeh 0.12.3rc2
OSX 10.11.6
Chrome Version 53.0.2785.143 (64-bit)



Discussion:
We should make sure tuple problems are confined to just these properties and not more generally a problem. 

With this definition of `Color`:

```
class Color extends simple_prop("Color", (x) ->
  svg_colors[x.toLowerCase()]? or x.substring(0, 1) == "#" or valid_rgb(x)
)
```

all properties are affected. Note that this definition contains bits (`rgb()` and `rgba()` syntax) that aren't support in Python and the same the other way (tuples).

@philippjfr 

In case it helps, I was able to do 

``` python
mapper = CategoricalColorMapper(
    palette=['blue', 'red'],
    factors=['c0', 'c1'],
    nan_color="rgba(255, 255, 255, 0.5)",
)
```

By changing Color spec: 
https://github.com/bokeh/bokeh/commit/0bd16231ecf33b8fe6cfd949ccee2a8ef9e35020

``` diff
diff --git a/bokeh/core/properties.py b/bokeh/core/properties.py
index 12a48cd..cb8aae6 100644
--- a/bokeh/core/properties.py
+++ b/bokeh/core/properties.py
@@ -1452,8 +1452,11 @@ class Color(Either):
     """

     def __init__(self, default=None, help=None):
+        rgb_int = "\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*"
         types = (Enum(enums.NamedColor),
                  Regex("^#[0-9a-fA-F]{6}$"),
+                 Regex("^rgb\\(%s,%s,%s\\)$" % (rgb_int, rgb_int, rgb_int)),
+                 Regex("^rgba\\(%s,%s,%s,\\s*((0.[1-9])|[01])\\s*\\)$" % (rgb_int, rgb_int, rgb_int)),
                  Tuple(Byte, Byte, Byte),
                  Tuple(Byte, Byte, Byte, Percent))
         super(Color, self).__init__(*types, default=default, help=help)
```

I'm guessing this isn't what we're going to want to do for bokeh, but worked in case you're interested.

We can just add a `transform` method to the python prop that converts to one of the currently supported wire formats? I don't personally care if the BokehJS prop doesn't accept "tuples" directly. Will try this approach. 

Resolving patch:
diff --git a/bokeh/core/properties.py b/bokeh/core/properties.py
--- a/bokeh/core/properties.py
+++ b/bokeh/core/properties.py
@@ -68,6 +68,7 @@ class Model(HasProps):
 
 from six import string_types, iteritems, StringIO
 
+from ..colors import RGB
 from ..util.dependencies import import_optional
 from ..util.deprecation import deprecated
 from ..util.future import with_metaclass
@@ -1504,6 +1505,11 @@ def __init__(self, default=None, help=None):
                  Tuple(Byte, Byte, Byte, Percent))
         super(Color, self).__init__(*types, default=default, help=help)
 
+    def transform(self, value):
+        if isinstance(value, tuple):
+            value = RGB(*value).to_css()
+        return value
+
     def __str__(self):
         return self.__class__.__name__
 


Test patch:
diff --git a/bokeh/core/tests/test_properties.py b/bokeh/core/tests/test_properties.py
--- a/bokeh/core/tests/test_properties.py
+++ b/bokeh/core/tests/test_properties.py
@@ -1409,6 +1409,9 @@ def test_Color(self):
         self.assertTrue(prop.is_valid("BLUE"))
         self.assertFalse(prop.is_valid("foobar"))
 
+        self.assertEqual(prop.transform((0, 127, 255)), "rgb(0, 127, 255)")
+        self.assertEqual(prop.transform((0, 127, 255, 0.1)), "rgba(0, 127, 255, 0.1)")
+
     def test_Align(self):
         prop = Align() # TODO
         assert prop