text: """This is the Bokeh charts interface. It gives you a high level API to build
  complex plot is a simple way.
  
  This is the ChartObject class, a minimal prototype class to build more chart
  types on top of it. It provides the mechanisms to support the shared chained
  methods.
  """
  #-----------------------------------------------------------------------------
  # Copyright (c) 2012 - 2014, Continuum Analytics, Inc. All rights reserved.
  #
  # Powered by the Bokeh Development Team.
  #
  # The full license is in the file LICENSE.txt, distributed with this software.
  #-----------------------------------------------------------------------------
  
  #-----------------------------------------------------------------------------
  # Imports
  #-----------------------------------------------------------------------------
  
  from __future__ import absolute_import
  
  from six import string_types
  from collections import OrderedDict
  from ..properties import bokeh_integer_types, Datetime
  
  try:
      import numpy as np
  
  except ImportError:
      np = None
  
  try:
      import pandas as pd
  
  except ImportError:
      pd = None
  
  #-----------------------------------------------------------------------------
  # Classes and functions
  #-----------------------------------------------------------------------------
  
  DEFAULT_INDEX_ALIASES = list('abcdefghijklmnopqrstuvz1234567890')
  DEFAULT_INDEX_ALIASES += list(zip(DEFAULT_INDEX_ALIASES, DEFAULT_INDEX_ALIASES))
  
  class DataAdapter(object):
      """
      Adapter object used to normalize Charts inputs to a common interface.
      Supported inputs are dict, list, tuple, np.ndarray and pd.DataFrame.
      """
      def __init__(self, data, index=None, columns=None, force_alias=True):
          self._values = self.validate_values(data)
  
          self.convert_index_to_int = False
          self._columns_map = {}
          self.convert_items_to_dict = False
  
          if columns is None and force_alias:
              # no column 'labels' defined for data... in this case we use
              # default names
              keys = getattr(self._values, 'keys', None)
              if callable(keys):
                  columns = list(keys())
  
              elif keys is None:
                  columns = list(map(str, range(len(data))))
  
              else:
                  columns = list(keys)
  
          if columns:
              self._columns = columns
  
              # define a mapping between the real keys to access data and the aliases
              # we have defined using 'columns'
              self._columns_map = dict(zip(columns, self.keys()))
  
          if index is not None:
              self._index = index
              self.convert_items_to_dict = True
  
          elif force_alias:
              _index = getattr(self._values, 'index', None)
  
              # check because if it is a callable self._values is not a
              # dataframe (probably a list)
              if _index is None:
                  indexes = self.index
  
                  if isinstance(indexes[0], int):
                      self._index = DEFAULT_INDEX_ALIASES[:][:len(self.values()[0])]
                      self.convert_items_to_dict = True
  
              elif not callable(_index):
                  self._index = list(_index)
                  self.convert_items_to_dict = True
  
              else:
                  self._index = DEFAULT_INDEX_ALIASES[:][:len(self.values()[0])]
                  self.convert_items_to_dict = True
  
      @staticmethod
      def is_number(value):
          numbers = (float, ) + bokeh_integer_types
          return isinstance(value, numbers)
  
      @staticmethod
      def is_datetime(value):
          try:
              dt = Datetime(value)
              dt # shut up pyflakes
              return True
  
          except ValueError:
              return False
  
      @staticmethod
      def validate_values(values):
          if np and isinstance(values, np.ndarray):
              if len(values.shape) == 1:
                  return np.array([values])
  
              else:
                  return values
  
          elif pd and isinstance(values, pd.DataFrame):
              return values
  
          elif isinstance(values, (dict, OrderedDict)):
              if all(DataAdapter.is_number(x) for x in values.values()):
                  return values
  
              return values
  
          elif isinstance(values, (list, tuple)):
              if all(DataAdapter.is_number(x) for x in values):
                  return [values]
  
              return values
  
          # TODO: Improve this error message..
          raise TypeError("Input type not supported! %s" % values)
  
  
      def index_converter(self, x):
          key = self._columns_map.get(x, x)
          if self.convert_index_to_int:
              key = int(key)
          return key
  
      def keys(self):
          # assuming it's a dict or dataframe
          keys = getattr(self._values, "keys", None)
  
          if callable(keys):
              return list(keys())
  
          elif keys is None:
              # assuming that only non-dict like objects can raise this error
              # it's probably because we have an iterable instead of a mapper
              # in this case let's use indices as groups keys
              self.convert_index_to_int = True
              indexes = range(len(self._values))
              return list(map(str, indexes))
  
          else:
              return list(keys)
  
      def __len__(self):
          return len(self.values())
  
      def __iter__(self):
          for k in self.keys():
              yield k
  
      def __getitem__(self, key):
          val = self._values[self.index_converter(key)]
  
          # if we have "index aliases" we need to remap the values...
          if self.convert_items_to_dict:
              val = dict(zip(self._index, val))
  
          return val
  
      def values(self):
          values = getattr(self._values, "values", None)
  
          if callable(values):
              return list(values())
  
          elif values is None:
              return self._values
  
          else:
              # assuming it's a dataframe, in that case it returns transposed
              # values compared to it's dict equivalent..
              return list(values.T)
  
      def items(self):
          return [(key, self[key]) for key in self]
  
      def iterkeys(self):
          return iter(self)
  
      def itervalues(self):
          for k in self:
              yield self[k]
  
      def iteritems(self):
          for k in self:
              yield (k, self[k])
  
      @property
      def columns(self):
          try:
              return self._columns
  
          except AttributeError:
              return list(self.keys())
  
      @property
      def index(self):
          try:
              return self._index
  
          except AttributeError:
              index = getattr(self._values, "index", None)
  
              if not callable(index) and index is not None:
                  # guess it's a pandas dataframe..
                  return index
  
          # no, it's not. So it's probably a list so let's get the
          # values and check
          values = self.values()
  
          if isinstance(values, dict):
              return list(values.keys())
  
          else:
              first_el = self.values()[0]
  
              if isinstance(first_el, dict):
                  indexes = list(first_el.keys())
  
              else:
                  indexes = range(0, len(self.values()[0]))
                  self._index = indexes
              return indexes
  
      #-----------------------------------------------------------------------------
      # Convenience methods
      #-----------------------------------------------------------------------------
      @staticmethod
      def get_index_and_data(values, index=None):
          """Parse values (that must be one of the DataAdapter supported
          input types) and create an separate/create index and data
          depending on values type and index.
  
          Args:
              values (iterable): container that holds data to be plotted using
                  on the Chart classes
  
          Returns:
              xs: iterable that represents the data index
              values: iterable containing the values to be plotted
          """
          if hasattr(values, 'keys'):
              if index is not None:
                  if isinstance(index, string_types):
                      xs = values[index]
  
                  else:
                      xs = index
  
              else:
                  try:
                      xs = values.index
  
                  except AttributeError:
                      values = DataAdapter(values, force_alias=False)
                      xs = values.index
  
          else:
              if index is None:
                  values = DataAdapter(values, force_alias=False)
                  xs = values.index
  
              elif isinstance(index, string_types):
                  msg = "String indexes are only supported for DataFrame and dict inputs"
                  raise TypeError(msg)
  
              else:
                  xs = index
                  values = DataAdapter(values, force_alias=False)
  
          return xs, values
  