       else
         return obj_as_string
 
+  # Returns an array containing the valid indices of the input array arr.
   indices = (arr) ->
     return _.range(arr.length)
 
+  # Returns the index of the minimum element of an array.
   argmin = (arr) ->
     ret = _.min(indices(arr), ((i) -> return arr[i]))
     return ret
 
+  # Forces a number x into a specified range [min_val, max_val].
   clamp = (x, min_val, max_val) ->
     return Math.max(min_val, Math.min(max_val, x))
 
+  # A log function with an optional base.
   log = (x, base=Math.E) ->
     return Math.log(x) / Math.log(base)
 
+  # ---------------------------------------------------------------------------
+  # Date/time utility functions
+  # ---------------------------------------------------------------------------
+
+  # Makes a copy of a date object.
+  copy_date = (date) ->
+    return new Date(date.getTime())
+
+  # Rounds a date down to the month.
+  last_month_no_later_than = (date) ->
+    date = copy_date(date)
+    date.setUTCDate(1)
+    date.setUTCHours(0)
+    date.setUTCMinutes(0)
+    date.setUTCSeconds(0)
+    date.setUTCMilliseconds(0)
+    return date
+
+
+  # Rounds a date down to the year.
+  last_year_no_later_than = (date) ->
+    date = last_month_no_later_than(date)
+    date.setUTCMonth(0)
+    return date
+
+  # Given a start and end time in millis, returns the shortest array of
+  # consecutive years (as Dates) that surrounds both times.
+  date_range_by_year = (start_time, end_time) ->
+    start_date = last_year_no_later_than(new Date(start_time))
+
+    end_date = last_year_no_later_than(new Date(end_time))
+    end_date.setUTCFullYear(end_date.getUTCFullYear() + 1)
+
+    dates = []
+    date = start_date
+    while true
+      dates.push(copy_date(date))
+
+      date.setUTCFullYear(date.getUTCFullYear() + 1)
+      if date > end_date
+        break
+
+    return dates
+
+  # Given a start and end time in millis, returns the shortest array of
+  # consecutive months (as Dates) that surrounds both times.
+  date_range_by_month = (start_time, end_time) ->
+    start_date = last_month_no_later_than(new Date(start_time))
+
+    end_date = last_month_no_later_than(new Date(end_time))
+    # XXX This is not a reliable technique in general, but it should be
+    # safe when the day of the month is 1.  (The problem case is this:
+    # Mar 31 -> Apr 31, which becomes May 1.)
+    prev_end_date = copy_date(end_date)
+    end_date.setUTCMonth(end_date.getUTCMonth() + 1)
+
+    dates = []
+    date = start_date
+    while true
+      dates.push(copy_date(date))
+
+      date.setUTCMonth(date.getUTCMonth() + 1)
+      if date > end_date
+        break
+
+    return dates
+
+  # ---------------------------------------------------------------------------
+  # Scale classes
+  # ---------------------------------------------------------------------------
+
   DEFAULT_DESIRED_N_TICKS = 6
 
+  # The base class for all Scale objects.  It needs to be subclassed before
+  # being used.  The simplest subclass is SingleIntervalScale.
+  #
+  # The main value of a Scale is its get_ticks() method, which takes a min and
+  # max value and (optionally) a desired number of ticks, and returns an array
+  # of approximately that many ticks, evenly spaced, with nice round values,
+  # within that range.
+  # 
+  # Different Scales are suited to different types of data or different 
+  # magnitudes.  To make it possible to select Scales programmatically, they
+  # also support some additional methods: get_interval(), get_min_interval(),
+  # and get_max_interval().
   class AbstractScale
+    # Creates a new AbstractScale.  The toString_properties argument is an
+    # optional list of member names which be shown when toString() is called.
     constructor: (@toString_properties=[]) ->
 
+    # Generates a nice series of ticks for a given range.
     get_ticks: (data_low, data_high,
                 desired_n_ticks=DEFAULT_DESIRED_N_TICKS) ->
       return @get_ticks_no_defaults(data_low, data_high, desired_n_ticks)
 
-    get_min_interval: () -> @min_interval
-
-    get_max_interval: () -> @max_interval
-
-    toString: () ->
-      # FIXME Should we use typeof() instead of constructor.name?
-      class_name = @constructor.name
-      props = @toString_properties
-      params_str = ("#{key}=#{repr(this[key])}" for key in props).join(", ")
-      return "#{class_name}(#{params_str})"
-
-    min_interval: undefined
-
-    max_interval: undefined
-
-    get_ideal_interval: (data_low, data_high, desired_n_ticks) ->
-      data_range = data_high - data_low
-      return data_range / desired_n_ticks
-
-    get_interval: (data_low, data_high, desired_n_ticks) ->
-      return @get_interval(data_low, data_high, desired_n_ticks)
-
+    # The version of get_ticks() that does the work (and the version that
+    # should be overridden in subclasses).
     get_ticks_no_defaults: (data_low, data_high, desired_n_ticks) ->
       interval = @get_interval(data_low, data_high, desired_n_ticks)
       start_factor = Math.floor(data_low / interval)

        # Catchall for large timescales.
        new AdaptiveScale([1, 2, 5], 10, ONE_YEAR, Infinity),
      ])

  class BasicTickFormatter
    constructor: (@precision='auto', @use_scientific=true, @power_limit_high=5, @power_limit_low=-3) ->


      return ticks_in_range

  # A DaysScale produces ticks from a fixed subset of calendar days.
  # E.g., DaysScale([1, 15]) produces ticks on the 1st and 15th days of each
  # month.
  class DaysScale extends SingleIntervalScale
    constructor: (@days) ->
      @typical_interval = if @days.length > 1
