From 481abd90f9f10b633e7fb236b86aeebaf1fd9004 Mon Sep 17 00:00:00 2001
From: "Anonymous@example.com" <cpsievert1@gmail.com>
Date: Mon, 16 Feb 2015 12:05:56 -0600
Subject: [PATCH 1/9] Ignore RStudio projects

---
 .gitignore | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/.gitignore b/.gitignore
index e2325cfa68..589e7d2963 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,3 +5,5 @@ Rapp.history
 .Rhistory
 .RData
 Makefile
+.Rproj.user
+*.Rproj

From c1742df802f04f3d66b9e61b47dada35293a715b Mon Sep 17 00:00:00 2001
From: "Anonymous@example.com" <cpsievert1@gmail.com>
Date: Tue, 17 Feb 2015 18:12:37 -0600
Subject: [PATCH 2/9] update docs

---
 NAMESPACE                    |  2 +-
 man/ensure_file_exist.Rd     | 15 +++++++++++++++
 man/get_config_file.Rd       | 23 +++++++++++++++++++++++
 man/get_credentials_file.Rd  | 23 +++++++++++++++++++++++
 man/gg2list.Rd               |  3 ++-
 man/ggplot_build2.Rd         |  3 ++-
 man/group2NA.Rd              |  3 ++-
 man/layer2traces.Rd          |  3 ++-
 man/paramORdefault.Rd        |  3 ++-
 man/plotly-package.Rd        |  5 +++--
 man/plotly.Rd                |  3 ++-
 man/set_config_file.Rd       |  3 ++-
 man/set_credentials_file.Rd  |  3 ++-
 man/show_config_file.Rd      |  3 ++-
 man/show_credentials_file.Rd |  3 ++-
 man/signup.Rd                |  3 ++-
 man/toFill.Rd                |  7 +++++--
 man/toRGB.Rd                 |  5 +++--
 18 files changed, 95 insertions(+), 18 deletions(-)
 create mode 100644 man/ensure_file_exist.Rd
 create mode 100644 man/get_config_file.Rd
 create mode 100644 man/get_credentials_file.Rd

diff --git a/NAMESPACE b/NAMESPACE
index dc6b9f52c4..50bca0d434 100644
--- a/NAMESPACE
+++ b/NAMESPACE
@@ -1,4 +1,4 @@
-# Generated by roxygen2 (4.0.2): do not edit by hand
+# Generated by roxygen2 (4.1.0): do not edit by hand
 
 export(gg2list)
 export(ggplot_build2)
diff --git a/man/ensure_file_exist.Rd b/man/ensure_file_exist.Rd
new file mode 100644
index 0000000000..ab1c577a2f
--- /dev/null
+++ b/man/ensure_file_exist.Rd
@@ -0,0 +1,15 @@
+% Generated by roxygen2 (4.1.0): do not edit by hand
+% Please edit documentation in R/tools.R
+\name{ensure_file_exist}
+\alias{ensure_file_exist}
+\title{Create file if nonexistent}
+\usage{
+ensure_file_exist(abspath)
+}
+\arguments{
+\item{abspath}{Character vector of file path}
+}
+\description{
+Create file if nonexistent
+}
+
diff --git a/man/get_config_file.Rd b/man/get_config_file.Rd
new file mode 100644
index 0000000000..55de20aa9e
--- /dev/null
+++ b/man/get_config_file.Rd
@@ -0,0 +1,23 @@
+% Generated by roxygen2 (4.1.0): do not edit by hand
+% Please edit documentation in R/tools.R
+\name{get_config_file}
+\alias{get_config_file}
+\title{Read Plotly config file (which is a JSON) and create one if nonexistent}
+\usage{
+get_config_file(args = c())
+}
+\arguments{
+\item{args}{Character vector of keys you are looking up}
+}
+\value{
+List of keyword-value pairs (config)
+}
+\description{
+Read Plotly config file (which is a JSON) and create one if nonexistent
+}
+\examples{
+\dontrun{
+get_config_file(c("plotly_domain", "plotly_streaming_domain"))
+}
+}
+
diff --git a/man/get_credentials_file.Rd b/man/get_credentials_file.Rd
new file mode 100644
index 0000000000..4c3178d002
--- /dev/null
+++ b/man/get_credentials_file.Rd
@@ -0,0 +1,23 @@
+% Generated by roxygen2 (4.1.0): do not edit by hand
+% Please edit documentation in R/tools.R
+\name{get_credentials_file}
+\alias{get_credentials_file}
+\title{Read Plotly credentials file (which is a JSON)}
+\usage{
+get_credentials_file(args = c())
+}
+\arguments{
+\item{args}{Character vector of keys you are looking up}
+}
+\value{
+List of keyword-value pairs (credentials)
+}
+\description{
+Read Plotly credentials file (which is a JSON)
+}
+\examples{
+\dontrun{
+get_credentials_file(c("username", "api_key"))
+}
+}
+
diff --git a/man/gg2list.Rd b/man/gg2list.Rd
index c3080229a6..67016d2a0d 100644
--- a/man/gg2list.Rd
+++ b/man/gg2list.Rd
@@ -1,4 +1,5 @@
-% Generated by roxygen2 (4.0.2): do not edit by hand
+% Generated by roxygen2 (4.1.0): do not edit by hand
+% Please edit documentation in R/ggplotly.R
 \name{gg2list}
 \alias{gg2list}
 \title{Convert a ggplot to a list.}
diff --git a/man/ggplot_build2.Rd b/man/ggplot_build2.Rd
index 5125cfff1d..a37efa75c4 100644
--- a/man/ggplot_build2.Rd
+++ b/man/ggplot_build2.Rd
@@ -1,4 +1,5 @@
-% Generated by roxygen2 (4.0.2): do not edit by hand
+% Generated by roxygen2 (4.1.0): do not edit by hand
+% Please edit documentation in R/build_function.R
 \name{ggplot_build2}
 \alias{ggplot_build2}
 \title{ggplot build function with enhanced return}
diff --git a/man/group2NA.Rd b/man/group2NA.Rd
index faeb97f3f8..c1114f4c35 100644
--- a/man/group2NA.Rd
+++ b/man/group2NA.Rd
@@ -1,4 +1,5 @@
-% Generated by roxygen2 (4.0.2): do not edit by hand
+% Generated by roxygen2 (4.1.0): do not edit by hand
+% Please edit documentation in R/trace_generation.R
 \name{group2NA}
 \alias{group2NA}
 \title{Drawing ggplot2 geoms with a group aesthetic is most efficient in
diff --git a/man/layer2traces.Rd b/man/layer2traces.Rd
index b0e80b9bf0..d57083fe5b 100644
--- a/man/layer2traces.Rd
+++ b/man/layer2traces.Rd
@@ -1,4 +1,5 @@
-% Generated by roxygen2 (4.0.2): do not edit by hand
+% Generated by roxygen2 (4.1.0): do not edit by hand
+% Please edit documentation in R/trace_generation.R
 \name{layer2traces}
 \alias{layer2traces}
 \title{Convert a layer to a list of traces. Called from gg2list()}
diff --git a/man/paramORdefault.Rd b/man/paramORdefault.Rd
index 491d3cbcd9..c0cb1cb457 100644
--- a/man/paramORdefault.Rd
+++ b/man/paramORdefault.Rd
@@ -1,4 +1,5 @@
-% Generated by roxygen2 (4.0.2): do not edit by hand
+% Generated by roxygen2 (4.1.0): do not edit by hand
+% Please edit documentation in R/marker_conversion.R
 \name{paramORdefault}
 \alias{paramORdefault}
 \title{Convert ggplot params to plotly.}
diff --git a/man/plotly-package.Rd b/man/plotly-package.Rd
index 91ca574ac1..2643bf2dd2 100644
--- a/man/plotly-package.Rd
+++ b/man/plotly-package.Rd
@@ -1,4 +1,5 @@
-% Generated by roxygen2 (4.0.2): do not edit by hand
+% Generated by roxygen2 (4.1.0): do not edit by hand
+% Please edit documentation in R/plotly-package.r
 \docType{package}
 \name{plotly-package}
 \alias{plotly-package}
@@ -14,7 +15,7 @@ An example of an interactive graph made from the R API: https://plot.ly/~chris/4
 \itemize{
  \item Package: plotly
  \item Type: Package
- \item Version: 0.3.4
+ \item Version: 0.5.20
  \item Date: 2014-03-07
  \item License: MIT
 }
diff --git a/man/plotly.Rd b/man/plotly.Rd
index 6119c5a751..0c4db5bd35 100644
--- a/man/plotly.Rd
+++ b/man/plotly.Rd
@@ -1,4 +1,5 @@
-% Generated by roxygen2 (4.0.2): do not edit by hand
+% Generated by roxygen2 (4.1.0): do not edit by hand
+% Please edit documentation in R/plotly.R
 \name{plotly}
 \alias{plotly}
 \title{Main interface to plotly}
diff --git a/man/set_config_file.Rd b/man/set_config_file.Rd
index 59fde04bbf..d37aaf672b 100644
--- a/man/set_config_file.Rd
+++ b/man/set_config_file.Rd
@@ -1,4 +1,5 @@
-% Generated by roxygen2 (4.0.2): do not edit by hand
+% Generated by roxygen2 (4.1.0): do not edit by hand
+% Please edit documentation in R/tools.R
 \name{set_config_file}
 \alias{set_config_file}
 \title{Set keyword-value pairs in Plotly config file}
diff --git a/man/set_credentials_file.Rd b/man/set_credentials_file.Rd
index 3f398d1e18..b5145446c8 100644
--- a/man/set_credentials_file.Rd
+++ b/man/set_credentials_file.Rd
@@ -1,4 +1,5 @@
-% Generated by roxygen2 (4.0.2): do not edit by hand
+% Generated by roxygen2 (4.1.0): do not edit by hand
+% Please edit documentation in R/tools.R
 \name{set_credentials_file}
 \alias{set_credentials_file}
 \title{Set the keyword-value pairs in Plotly credentials file}
diff --git a/man/show_config_file.Rd b/man/show_config_file.Rd
index 178dbab4a9..1f781075f7 100644
--- a/man/show_config_file.Rd
+++ b/man/show_config_file.Rd
@@ -1,4 +1,5 @@
-% Generated by roxygen2 (4.0.2): do not edit by hand
+% Generated by roxygen2 (4.1.0): do not edit by hand
+% Please edit documentation in R/tools.R
 \name{show_config_file}
 \alias{show_config_file}
 \title{Read and print Plotly config file, wrapping get_credentials_file()}
diff --git a/man/show_credentials_file.Rd b/man/show_credentials_file.Rd
index e4c2072311..70a9bda657 100644
--- a/man/show_credentials_file.Rd
+++ b/man/show_credentials_file.Rd
@@ -1,4 +1,5 @@
-% Generated by roxygen2 (4.0.2): do not edit by hand
+% Generated by roxygen2 (4.1.0): do not edit by hand
+% Please edit documentation in R/tools.R
 \name{show_credentials_file}
 \alias{show_credentials_file}
 \title{Read and print Plotly credentials file, wrapping get_credentials_file()}
diff --git a/man/signup.Rd b/man/signup.Rd
index 226094019d..577d1d1c5d 100644
--- a/man/signup.Rd
+++ b/man/signup.Rd
@@ -1,4 +1,5 @@
-% Generated by roxygen2 (4.0.2): do not edit by hand
+% Generated by roxygen2 (4.1.0): do not edit by hand
+% Please edit documentation in R/signup.R
 \name{signup}
 \alias{signup}
 \title{Sign up to plotly.}
diff --git a/man/toFill.Rd b/man/toFill.Rd
index abc71b77ec..05e3f49dff 100644
--- a/man/toFill.Rd
+++ b/man/toFill.Rd
@@ -1,12 +1,15 @@
-% Generated by roxygen2 (4.0.2): do not edit by hand
+% Generated by roxygen2 (4.1.0): do not edit by hand
+% Please edit documentation in R/colour_conversion.R
 \name{toFill}
 \alias{toFill}
 \title{Use default ggplot colour for fill (gray20) if not declared}
 \usage{
-toFill(x)
+toFill(x, alpha = 1)
 }
 \arguments{
 \item{x}{character for colour}
+
+\item{alpha}{transparency alpha}
 }
 \value{
 hexadecimal colour value
diff --git a/man/toRGB.Rd b/man/toRGB.Rd
index c0cac1536c..5e75af9c0b 100644
--- a/man/toRGB.Rd
+++ b/man/toRGB.Rd
@@ -1,4 +1,5 @@
-% Generated by roxygen2 (4.0.2): do not edit by hand
+% Generated by roxygen2 (4.1.0): do not edit by hand
+% Please edit documentation in R/colour_conversion.R
 \name{toRGB}
 \alias{toRGB}
 \title{Convert R colours to RGBA hexadecimal colour values}
@@ -8,7 +9,7 @@ toRGB(x, alpha = 1)
 \arguments{
 \item{x}{character for colour, for example: "white"}
 
-\item{alpha}{alpha}
+\item{alpha}{transparency alpha}
 }
 \value{
 hexadecimal colour value (if is.na(x), return "transparent" for compatibility with Plotly)

From 12f471a3e642eb112cfacbfcd988c2cab33a0a0f Mon Sep 17 00:00:00 2001
From: "Anonymous@example.com" <cpsievert1@gmail.com>
Date: Tue, 17 Feb 2015 18:14:53 -0600
Subject: [PATCH 3/9] Implement free scales for facet_wrap. Fixes #166

---
 R/ggplotly.R                 | 314 +++++++++++++++++++----------------
 tests/testthat/test-facets.R |  46 +++++
 2 files changed, 217 insertions(+), 143 deletions(-)

diff --git a/R/ggplotly.R b/R/ggplotly.R
index 868122718f..cf26cf407f 100644
--- a/R/ggplotly.R
+++ b/R/ggplotly.R
@@ -30,19 +30,19 @@ polygon.line.defaults$colour <- NA
 lty2dash <- c(numeric.lty, named.lty, coded.lty)
 
 aesConverters <- list(linetype=function(lty) {
-                        lty2dash[as.character(lty)]
-                      },
-                      colour=function(col) {
-                        toRGB(col)
-                      },
-                      size=identity,
-                      sizeref=identity,
-                      sizemode=identity,
-                      alpha=identity,
-                      shape=function(pch) {
-                        pch2symbol[as.character(pch)]
-                      },
-                      direction=identity)
+  lty2dash[as.character(lty)]
+},
+colour=function(col) {
+  toRGB(col)
+},
+size=identity,
+sizeref=identity,
+sizemode=identity,
+alpha=identity,
+shape=function(pch) {
+  pch2symbol[as.character(pch)]
+},
+direction=identity)
 
 markLegends <-
   ## NOTE: Do we also want to split on size?
@@ -168,6 +168,9 @@ gg2list <- function(p){
     gglayout <- built$panel$layout
     ## invert rows so that plotly and ggplot2 show panels in the same order
     gglayout$plotly.row <- max(gglayout$ROW) - gglayout$ROW + 1
+    ## ugh, ggplot counts panel right-to-left & top-to-bottom
+    ## plotly count them right-to-left & *bottom-to-top*
+    gglayout$plotly.panel <- with(gglayout, order(plotly.row, COL))
     
     ## Add ROW and COL to df: needed to link axes to traces; keep df's
     ## original ordering while merging.
@@ -175,7 +178,7 @@ gg2list <- function(p){
     df <- merge(df, gglayout[, c("PANEL", "plotly.row", "COL")])
     df <- df[order(df$order),]
     df$order <- NULL
-
+    
     misc$prestats.data <- merge(built$prestats.data[[i]],
                                 gglayout[, c("PANEL", "plotly.row", "COL")])
     
@@ -191,7 +194,7 @@ gg2list <- function(p){
       misc$prestats.data$globsizemin <- ggsizemin
       misc$prestats.data$globsizemax <- ggsizemax
     }
-
+    
     ## This extracts essential info for this geom/layer.
     traces <- layer2traces(L, df, misc)
     
@@ -219,7 +222,7 @@ gg2list <- function(p){
       trace.list <- c(trace.list, traces)
     }
   }
-
+  
   ## for barcharts, verify that all traces have the same barmode; we don't
   ## support different barmodes on the same plot yet.
   barmodes <- do.call(c, lapply(trace.list, function (x) x$barmode))
@@ -278,12 +281,12 @@ gg2list <- function(p){
     grid <- theme.pars$panel.grid
     grid.major <- theme.pars$panel.grid.major
     if ((!is.null(grid$linetype) || !is.null(grid.major$linetype)) && 
-          c(grid$linetype, grid.major$linetype) %in% c(2, 3, "dashed", "dotted")) {
+        c(grid$linetype, grid.major$linetype) %in% c(2, 3, "dashed", "dotted")) {
       ax.list$gridcolor <- ifelse(is.null(grid.major$colour),
-                                 toRGB(grid$colour, 0.1),
-                                 toRGB(grid.major$colour, 0.1))
+                                  toRGB(grid$colour, 0.1),
+                                  toRGB(grid.major$colour, 0.1))
     } else {
-       ax.list$gridcolor <- toRGB(grid.major$colour)
+      ax.list$gridcolor <- toRGB(grid.major$colour)
     }
     
     ax.list$showgrid <- !is.blank(s("panel.grid.major.%s"))
@@ -349,7 +352,6 @@ gg2list <- function(p){
     !is.blank(s("axis.line.%s"))
     layout[[s("%saxis")]] <- ax.list
   }
-
   ## copy [x/y]axis to [x/y]axisN and set domain, range, etc. for each
   xaxis.title <- layout$xaxis$title
   yaxis.title <- layout$yaxis$title
@@ -357,130 +359,156 @@ gg2list <- function(p){
   outer.margin <- 0.05 ## to put titles outside of the plots
   orig.xaxis <- layout$xaxis
   orig.yaxis <- layout$yaxis
-  if (nrow(gglayout) > 1)
-    {
-      row.size <- 1. / max(gglayout$ROW)
-      col.size <- 1. / max(gglayout$COL)
-      for (i in seq_len(nrow(gglayout)))
-        {
-          row <- gglayout[i, "plotly.row"] 
-          col <- gglayout[i, "COL"] 
-          x <- col * col.size
-          xmin <- x - col.size
-          xmax <- x - inner.margin
-          y <- row * row.size
-          ymin <- y - row.size
-          ymax <- y - inner.margin
-          if ("wrap" %in% class(p$facet))
-            ymax <- ymax - 0.04
-          yaxis.name <- if (row == 1) "yaxis" else paste0("yaxis", row)
-          xaxis.name <- if (col == 1) "xaxis" else paste0("xaxis", col)
-          layout[[xaxis.name]] <- orig.xaxis
-          layout[[xaxis.name]]$domain <- c(xmin, xmax)
-          layout[[xaxis.name]]$anchor <- "y"
-          layout[[xaxis.name]]$title <- NULL
-          if (orig.xaxis$type == "linear" && # range only makes sense for numeric data
-              (is.null(p$facet$scales) || p$facet$scales == "fixed" || p$facet$scales == "free_y"))
-            {
-              layout[[xaxis.name]]$range <- built$panel$ranges[[i]]$x.range
-              layout[[xaxis.name]]$autorange <- FALSE
-            }
-
-          layout[[yaxis.name]] <- orig.yaxis
-          layout[[yaxis.name]]$domain <- c(ymin, ymax)
-          layout[[yaxis.name]]$anchor <- "x"
-          layout[[yaxis.name]]$title <- NULL
-          if (orig.yaxis$type == "linear" && # range only makes sense for numeric data
-              (is.null(p$facet$scales) || p$facet$scales == "fixed" || p$facet$scales == "free_x"))
-            {
-              layout[[yaxis.name]]$range <- built$panel$ranges[[i]]$y.range
-              layout[[yaxis.name]]$autorange <- FALSE
-            }
-
+  if (nrow(gglayout) > 1) {
+    row.size <- 1. / max(gglayout$ROW)
+    col.size <- 1. / max(gglayout$COL)
+    npanels <- nrow(gglayout)
+    for (i in seq_len(npanels)) {
+      row <- gglayout[i, "plotly.row"]
+      col <- gglayout[i, "COL"]
+      panel <- gglayout[i, "plotly.panel"]
+      x <- col * col.size
+      xmin <- x - col.size
+      xmax <- x - inner.margin
+      y <- row * row.size
+      ymin <- y - row.size
+      ymax <- y - inner.margin
+      xaxis.name <- if (panel == 1) "xaxis" else paste0("xaxis", panel)
+      yaxis.name <- if (panel == 1) "yaxis" else paste0("yaxis", panel)
+      #layout defaults that won't change depending on the type of facet
+      layout[[xaxis.name]] <- orig.xaxis
+      layout[[xaxis.name]]$domain <- c(xmin, xmax)
+      layout[[xaxis.name]]$title <- NULL
+      layout[[yaxis.name]] <- orig.yaxis
+      layout[[yaxis.name]]$domain <- c(ymin, ymax)
+      layout[[yaxis.name]]$title <- NULL
+      # facet_wrap allows for a different x/y axis on each panel
+      if ("wrap" %in% class(p$facet)) {
+        # make room for facet strip label
+        ymax <- ymax - 0.04
+        # make room for yaxis labels (this should be a function of label size)
+        if (col == 1) {
+          xmax <- xmax - 0.02
+        } else {
+          xmin <- xmin + 0.02
         }
-      ## add panel titles as annotations
-      annotations <- list()
+        # make room for xaxis labels
+        if (row == 1) {
+          ymax <- ymax - 0.02
+        } else {
+          ymin <- ymin + 0.02
+        }
+        if (p$facet$free$y && panel > 1) {
+          # is it safe to assume npanels == ntraces?
+          yaxis.name <- paste0("yaxis", panel)
+          trace.list[[i]]$yaxis <- paste0("y", panel)
+          layout[[yaxis.name]]$anchor <- paste0("x", panel)
+        }
+        if (p$facet$free$x && panel > 1) {
+          xaxis.name <- paste0("xaxis", panel)
+          trace.list[[i]]$xaxis <- paste0("x", panel)
+          layout[[xaxis.name]]$anchor <- paste0("y", panel)
+        }
+        layout[[xaxis.name]]$domain <- c(xmin, xmax)
+        layout[[yaxis.name]]$domain <- c(ymin, ymax)
+      }
+      if (is.null(layout[[xaxis.name]]$anchor)) 
+        layout[[xaxis.name]]$anchor <- "y"
+      if (is.null(layout[[yaxis.name]]$anchor)) 
+        layout[[yaxis.name]]$anchor <- "x"
+      # range only makes sense for numeric data
+      if (orig.xaxis$type == "linear") {
+        layout[[xaxis.name]]$range <- built$panel$ranges[[i]]$x.range
+        layout[[xaxis.name]]$autorange <- FALSE
+      }
+      if (orig.yaxis$type == "linear") {
+        layout[[yaxis.name]]$range <- built$panel$ranges[[i]]$y.range
+        layout[[yaxis.name]]$autorange <- FALSE
+      }
+    }
+    ## add panel titles as annotations
+    annotations <- list()
+    nann <- 1
+    make.label <- function(text, x, y, xanchor="auto", yanchor="auto", textangle=0)
+      list(text=text, showarrow=FALSE, x=x, y=y, ax=0, ay=0, 
+           xref="paper", yref="paper", xanchor=xanchor, yanchor=yanchor, 
+           textangle=textangle)
+    
+    if ("grid" %in% class(p$facet))
+    {
+      frows <- names(p$facet$rows)
       nann <- 1
-      make.label <- function(text, x, y, xanchor="auto", yanchor="auto", textangle=0)
-        list(text=text, showarrow=FALSE, x=x, y=y, ax=0, ay=0, 
-             xref="paper", yref="paper", xanchor=xanchor, yanchor=yanchor, 
-             textangle=textangle)
       
-      if ("grid" %in% class(p$facet))
-        {
-          frows <- names(p$facet$rows)
-          nann <- 1
-          
-          for (i in seq_len(max(gglayout$ROW)))
-            {
-              text <- paste(lapply(gglayout[gglayout$ROW == i, frows, drop=FALSE][1,],
-                                   as.character),
-                            collapse=", ")
-              if (text != "") {  # to not create extra annotations
-                increase_margin_r <- TRUE
-                annotations[[nann]] <- make.label(text,
-                                                  1 + outer.margin - 0.04,
-                                                  row.size * (max(gglayout$ROW)-i+0.5),
-                                                  xanchor="center",
-                                                  textangle=90)
-                nann <- nann + 1
-              }
-            }
-          
-          fcols <- names(p$facet$cols)
-          for (i in seq_len(max(gglayout$COL)))
-            {
-              text <- paste(lapply(gglayout[gglayout$COL == i, fcols, drop=FALSE][1,],
-                                   as.character),
-                            collapse=", ")
-              if (text!="") {
-                annotations[[nann]] <- make.label(text,
-                                                  col.size * (i-0.5) - inner.margin/2,
-                                                  1 + outer.margin,
-                                                  xanchor="center")
-                nann <- nann + 1
-              }
-            }
-
-          ## add empty traces everywhere so that the background shows even if there
-          ## is no data for a facet
-          for (r in seq_len(max(gglayout$ROW)))
-            for (c in seq_len(max(gglayout$COL)))
-              trace.list <- c(trace.list, list(list(xaxis=paste0("x", c), yaxis=paste0("y", r), showlegend=FALSE)))
+      for (i in seq_len(max(gglayout$ROW)))
+      {
+        text <- paste(lapply(gglayout[gglayout$ROW == i, frows, drop=FALSE][1,],
+                             as.character),
+                      collapse=", ")
+        if (text != "") {  # to not create extra annotations
+          increase_margin_r <- TRUE
+          annotations[[nann]] <- make.label(text,
+                                            1 + outer.margin - 0.04,
+                                            row.size * (max(gglayout$ROW)-i+0.5),
+                                            xanchor="center",
+                                            textangle=90)
+          nann <- nann + 1
         }
-      else if ("wrap" %in% class(p$facet))
-        {
-          facets <- names(p$facet$facets)
-          for (i in seq_len(max(as.numeric(gglayout$PANEL))))
-            {
-              ix <- gglayout$PANEL == i
-              row <- gglayout$ROW[ix]
-              col <- gglayout$COL[ix]
-              text <- paste(lapply(gglayout[ix, facets, drop=FALSE][1,],
-                                   as.character),
-                            collapse=", ")
-              annotations[[nann]] <- make.label(text, 
-                                                col.size * (col-0.5) - inner.margin/2,
-                                                row.size * (max(gglayout$ROW) - row + 0.985),
-                                                xanchor="center",
-                                                yanchor="top")
-              nann <- nann + 1
-            }
-          }
-
-      ## axes titles
-      annotations[[nann]] <- make.label(xaxis.title, 
-                                        0.5, 
-                                        -outer.margin,
-                                        yanchor="top")
-      nann <- nann + 1
-      annotations[[nann]] <- make.label(yaxis.title, 
-                                        -outer.margin, 
-                                        0.5,
-                                        textangle=-90)
+      }
       
-      layout$annotations <- annotations
+      fcols <- names(p$facet$cols)
+      for (i in seq_len(max(gglayout$COL)))
+      {
+        text <- paste(lapply(gglayout[gglayout$COL == i, fcols, drop=FALSE][1,],
+                             as.character),
+                      collapse=", ")
+        if (text!="") {
+          annotations[[nann]] <- make.label(text,
+                                            col.size * (i-0.5) - inner.margin/2,
+                                            1 + outer.margin,
+                                            xanchor="center")
+          nann <- nann + 1
+        }
+      }
+      
+      ## add empty traces everywhere so that the background shows even if there
+      ## is no data for a facet
+      for (r in seq_len(max(gglayout$ROW)))
+        for (c in seq_len(max(gglayout$COL)))
+          trace.list <- c(trace.list, list(list(xaxis=paste0("x", c), yaxis=paste0("y", r), showlegend=FALSE)))
     }
+    else if ("wrap" %in% class(p$facet))
+    {
+      facets <- names(p$facet$facets)
+      for (i in seq_len(max(as.numeric(gglayout$PANEL))))
+      {
+        ix <- gglayout$PANEL == i
+        row <- gglayout$ROW[ix]
+        col <- gglayout$COL[ix]
+        text <- paste(lapply(gglayout[ix, facets, drop=FALSE][1,],
+                             as.character),
+                      collapse=", ")
+        annotations[[nann]] <- make.label(text, 
+                                          col.size * (col-0.5) - inner.margin/2,
+                                          row.size * (max(gglayout$ROW) - row + 0.985),
+                                          xanchor="center",
+                                          yanchor="top")
+        nann <- nann + 1
+      }
+    }
+    
+    ## axes titles
+    annotations[[nann]] <- make.label(xaxis.title, 
+                                      0.5, 
+                                      -outer.margin,
+                                      yanchor="top")
+    nann <- nann + 1
+    annotations[[nann]] <- make.label(yaxis.title, 
+                                      -outer.margin, 
+                                      0.5,
+                                      textangle=-90)
+    
+    layout$annotations <- annotations
+  }
   
   ## Remove legend if theme has no legend position
   layout$showlegend <- !(theme.pars$legend.position=="none")
@@ -505,7 +533,7 @@ gg2list <- function(p){
   # [markUnique != "x"] is for boxplot's particular case.
   if (any(names(layer.aes) %in% markUnique[markUnique != "x"]) == FALSE)
     layout$showlegend <- FALSE
-
+  
   if (layout$showlegend && length(p$data)) {
     # Retrieve legend title
     legend.elements <- sapply(traces, "[[", "name")
@@ -515,7 +543,7 @@ gg2list <- function(p){
         legend.title <- colnames(p$data)[i]
     }
     legend.title <- paste0("<b>", legend.title, "</b>")
-
+    
     # Create legend title element as an annotation
     if (exists("annotations")) {
       nann <- nann + 1
@@ -532,7 +560,7 @@ gg2list <- function(p){
                                 textangle=0)
     layout$annotations <- annotations
   }
-
+  
   ## Family font for text
   if (!is.null(theme.pars$text$family)) {
     layout$titlefont$family   <- theme.pars$text$family
@@ -601,7 +629,7 @@ gg2list <- function(p){
       }
     }
   }
-
+  
   # If background elements are NULL, and background rect (rectangle) is defined:
   rect_fill <- theme.pars$rect$fill
   if (!is.null(rect_fill)) {
diff --git a/tests/testthat/test-facets.R b/tests/testthat/test-facets.R
index 5ea8414484..a52348bff9 100644
--- a/tests/testthat/test-facets.R
+++ b/tests/testthat/test-facets.R
@@ -47,3 +47,49 @@ test_that("3 facets becomes 3 panels", {
   u <- unique(trace.axes.df)
   expect_identical(nrow(u), 3L)
 })
+
+# expect a certain number of _unique_ [x/y] axes
+expect_axes <- function(info, n, axis = "x") {
+  pattern <- paste0("^", axis, "axis([0-9]+)?$")
+  #n.axes <- length(grep(pattern, names(info$kwargs$layout)))
+  axes <- with(info$kwargs, layout[grepl(pattern, names(layout))])
+  n.axes <- length(axes)
+  ranges <- do.call("rbind", lapply(axes, function(x) x$range))
+  expect_identical(nrow(unique(ranges)), as.integer(n))
+}
+
+no_panels <- ggplot(mtcars, aes(mpg, wt)) + geom_point()
+
+test_that("facet_wrap(..., scales = 'free') creates interior scales", {
+  free_both <- no_panels + facet_wrap(~am+vs, scales = "free")
+  info <- gg2list(free_both)
+  expect_axes(info, 4L)
+  expect_axes(info, 4L, "y")
+  
+  free_y <- no_panels + facet_wrap(~am+vs, scales = "free_y")
+  info <- gg2list(free_y)
+  expect_axes(info, 1L)
+  expect_axes(info, 4L, "y")
+  
+  free_x <- no_panels + facet_wrap(~am+vs, scales = "free_x")
+  info <- gg2list(free_x)
+  expect_axes(info, 4L)
+  expect_axes(info, 1L, "y")
+})
+
+test_that("facet_grid(..., scales = 'free') doesnt create interior scales.", {
+  free_both <- no_panels + facet_grid(vs~am, scales = "free")
+  info <- gg2list(free_both)
+  expect_axes(info, 2L)
+  expect_axes(info, 2L, "y")
+  
+  free_y <- no_panels + facet_grid(vs~am, scales = "free_y")
+  info <- gg2list(free_y)
+  expect_axes(info, 1L)
+  expect_axes(info, 2L, "y")
+  
+  free_x <- no_panels + facet_grid(vs~am, scales = "free_x")
+  info <- gg2list(free_x)
+  expect_axes(info, 2L)
+  expect_axes(info, 1L, "y")
+})
\ No newline at end of file

From 19e42abde99ef0663e7adcb6a6a70deb6ae1278f Mon Sep 17 00:00:00 2001
From: "Anonymous@example.com" <cpsievert1@gmail.com>
Date: Tue, 17 Feb 2015 19:19:35 -0600
Subject: [PATCH 4/9] Fix identation of aesConverters

---
 R/ggplotly.R | 30 ++++++++++++++++--------------
 1 file changed, 16 insertions(+), 14 deletions(-)

diff --git a/R/ggplotly.R b/R/ggplotly.R
index cf26cf407f..6fe797c617 100644
--- a/R/ggplotly.R
+++ b/R/ggplotly.R
@@ -29,20 +29,22 @@ polygon.line.defaults$colour <- NA
 # Convert R lty line type codes to plotly "dash" codes.
 lty2dash <- c(numeric.lty, named.lty, coded.lty)
 
-aesConverters <- list(linetype=function(lty) {
-  lty2dash[as.character(lty)]
-},
-colour=function(col) {
-  toRGB(col)
-},
-size=identity,
-sizeref=identity,
-sizemode=identity,
-alpha=identity,
-shape=function(pch) {
-  pch2symbol[as.character(pch)]
-},
-direction=identity)
+aesConverters <- list(
+  linetype=function(lty) {
+    lty2dash[as.character(lty)]
+  },
+  colour=function(col) {
+    toRGB(col)
+  },
+  size=identity,
+  sizeref=identity,
+  sizemode=identity,
+  alpha=identity,
+  shape=function(pch) {
+    pch2symbol[as.character(pch)]
+  },
+  direction=identity
+)
 
 markLegends <-
   ## NOTE: Do we also want to split on size?

From b7346cd3b04597aa8f7858bff8ef7db5d2564577 Mon Sep 17 00:00:00 2001
From: "Anonymous@example.com" <cpsievert1@gmail.com>
Date: Fri, 20 Feb 2015 14:21:40 -0600
Subject: [PATCH 5/9] single pound for comments

---
 R/ggplotly.R | 142 +++++++++++++++++++++++++--------------------------
 1 file changed, 71 insertions(+), 71 deletions(-)

diff --git a/R/ggplotly.R b/R/ggplotly.R
index 6fe797c617..2bed029183 100644
--- a/R/ggplotly.R
+++ b/R/ggplotly.R
@@ -1,4 +1,4 @@
-## calc. the epoch
+# calc. the epoch
 now <- Sys.time()
 the.epoch <- now - as.numeric(now)
 
@@ -47,9 +47,9 @@ aesConverters <- list(
 )
 
 markLegends <-
-  ## NOTE: Do we also want to split on size?
-  ## Legends based on sizes not implemented yet in Plotly
-  ##  list(point=c("colour", "fill", "shape", "size"),
+  # NOTE: Do we also want to split on size?
+  # Legends based on sizes not implemented yet in Plotly
+  #  list(point=c("colour", "fill", "shape", "size"),
   list(point=c("colour", "fill", "shape"),
        path=c("linetype", "size", "colour", "shape"),
        polygon=c("colour", "fill", "linetype", "size", "group"),
@@ -70,10 +70,10 @@ gg2list <- function(p){
   if(length(p$layers) == 0) {
     stop("No layers in plot")
   }
-  ## Always use identity size scale so that plot.ly gets the real
-  ## units for the size variables.
+  # Always use identity size scale so that plot.ly gets the real
+  # units for the size variables.
   p <- tryCatch({
-    ## this will be an error for discrete variables.
+    # this will be an error for discrete variables.
     suppressMessages({
       ggplot_build(p+scale_size_continuous())
       p+scale_size_identity()
@@ -84,10 +84,10 @@ gg2list <- function(p){
   layout <- list()
   trace.list <- list()
   
-  ## Before building the ggplot, we would like to add aes(name) to
-  ## figure out what the object group is later. This also copies any
-  ## needed global aes/data values to each layer, so we do not have to
-  ## worry about combining global and layer-specific aes/data later.
+  # Before building the ggplot, we would like to add aes(name) to
+  # figure out what the object group is later. This also copies any
+  # needed global aes/data values to each layer, so we do not have to
+  # worry about combining global and layer-specific aes/data later.
   for(layer.i in seq_along(p$layers)) {
     layer.aes <- p$layers[[layer.i]]$mapping
     to.copy <- names(p$mapping)[!names(p$mapping) %in% names(layer.aes)]
@@ -101,7 +101,7 @@ gg2list <- function(p){
     }
   }
   
-  ## Extract data from built ggplots
+  # Extract data from built ggplots
   built <- ggplot_build2(p)
   
   # Get global x-range now because we need some of its info in layer2traces
@@ -123,20 +123,20 @@ gg2list <- function(p){
   }
   
   for(i in seq_along(built$plot$layers)){
-    ## This is the layer from the original ggplot object.
+    # This is the layer from the original ggplot object.
     L <- p$layers[[i]]
     
-    ## for each layer, there is a correpsonding data.frame which
-    ## evaluates the aesthetic mapping.
+    # for each layer, there is a correpsonding data.frame which
+    # evaluates the aesthetic mapping.
     df <- built$data[[i]]
     
-    ## Test fill and color to see if they encode a quantitative
-    ## variable. This may be useful for several reasons: (1) it is
-    ## sometimes possible to plot several different colors in the same
-    ## trace (e.g. points), and that is faster for large numbers of
-    ## data points and colors; (2) factors on x or y axes should be
-    ## sent to plotly as characters, not as numeric data (which is
-    ## what ggplot_build gives us).
+    # Test fill and color to see if they encode a quantitative
+    # variable. This may be useful for several reasons: (1) it is
+    # sometimes possible to plot several different colors in the same
+    # trace (e.g. points), and that is faster for large numbers of
+    # data points and colors; (2) factors on x or y axes should be
+    # sent to plotly as characters, not as numeric data (which is
+    # what ggplot_build gives us).
     misc <- list()
     for(a in c("fill", "colour", "x", "y")){
       for(data.type in c("continuous", "date", "datetime", "discrete")){
@@ -155,7 +155,7 @@ gg2list <- function(p){
       }
     }
     
-    ## scales are needed for legend ordering.
+    # scales are needed for legend ordering.
     for(sc in p$scales$scales){
       a <- sc$aesthetics
       if(length(a) == 1){
@@ -166,16 +166,16 @@ gg2list <- function(p){
       }
     }
     
-    ## get gglayout now because we need some of its info in layer2traces
+    # get gglayout now because we need some of its info in layer2traces
     gglayout <- built$panel$layout
-    ## invert rows so that plotly and ggplot2 show panels in the same order
+    # invert rows so that plotly and ggplot2 show panels in the same order
     gglayout$plotly.row <- max(gglayout$ROW) - gglayout$ROW + 1
-    ## ugh, ggplot counts panel right-to-left & top-to-bottom
-    ## plotly count them right-to-left & *bottom-to-top*
+    # ugh, ggplot counts panel right-to-left & top-to-bottom
+    # plotly count them right-to-left & *bottom-to-top*
     gglayout$plotly.panel <- with(gglayout, order(plotly.row, COL))
     
-    ## Add ROW and COL to df: needed to link axes to traces; keep df's
-    ## original ordering while merging.
+    # Add ROW and COL to df: needed to link axes to traces; keep df's
+    # original ordering while merging.
     df$order <- seq_len(nrow(df))
     df <- merge(df, gglayout[, c("PANEL", "plotly.row", "COL")])
     df <- df[order(df$order),]
@@ -197,7 +197,7 @@ gg2list <- function(p){
       misc$prestats.data$globsizemax <- ggsizemax
     }
     
-    ## This extracts essential info for this geom/layer.
+    # This extracts essential info for this geom/layer.
     traces <- layer2traces(L, df, misc)
     
     # Associate error bars with previous traces
@@ -225,8 +225,8 @@ gg2list <- function(p){
     }
   }
   
-  ## for barcharts, verify that all traces have the same barmode; we don't
-  ## support different barmodes on the same plot yet.
+  # for barcharts, verify that all traces have the same barmode; we don't
+  # support different barmodes on the same plot yet.
   barmodes <- do.call(c, lapply(trace.list, function (x) x$barmode))
   barmodes <- barmodes[!is.null(barmodes)]
   if (length(barmodes) > 0) {    
@@ -250,27 +250,27 @@ gg2list <- function(p){
     }
   }
   
-  ## Export axis specification as a combination of breaks and labels, on
-  ## the relevant axis scale (i.e. so that it can be passed into d3 on the
-  ## x axis scale instead of on the grid 0-1 scale). This allows
-  ## transformations to be used out of the box, with no additional d3
-  ## coding.
+  # Export axis specification as a combination of breaks and labels, on
+  # the relevant axis scale (i.e. so that it can be passed into d3 on the
+  # x axis scale instead of on the grid 0-1 scale). This allows
+  # transformations to be used out of the box, with no additional d3
+  # coding.
   theme.pars <- ggplot2:::plot_theme(p)
   
-  ## Flip labels if coords are flipped - transform does not take care
-  ## of this. Do this BEFORE checking if it is blank or not, so that
-  ## individual axes can be hidden appropriately, e.g. #1.
-  ## ranges <- built$panel$ranges[[1]]
-  ## if("flip"%in%attr(built$plot$coordinates, "class")){
-  ##   temp <- built$plot$labels$x
-  ##   built$plot$labels$x <- built$plot$labels$y
-  ##   built$plot$labels$y <- temp
-  ## }
+  # Flip labels if coords are flipped - transform does not take care
+  # of this. Do this BEFORE checking if it is blank or not, so that
+  # individual axes can be hidden appropriately, e.g. #1.
+  # ranges <- built$panel$ranges[[1]]
+  # if("flip"%in%attr(built$plot$coordinates, "class")){
+  #   temp <- built$plot$labels$x
+  #   built$plot$labels$x <- built$plot$labels$y
+  #   built$plot$labels$y <- temp
+  # }
   e <- function(el.name){
     ggplot2::calc_element(el.name, p$theme)
   }
   is.blank <- function(el.name, null.is.blank=FALSE) {
-    ## NULL shows ticks and hides borders
+    # NULL shows ticks and hides borders
     cls <- attr(e(el.name),"class")
     "element_blank" %in% cls || null.is.blank && is.null(cls)
   }
@@ -279,7 +279,7 @@ gg2list <- function(p){
     s <- function(tmp)sprintf(tmp, xy)
     ax.list$tickcolor <- toRGB(theme.pars$axis.ticks$colour)
     
-    ## When gridlines are dotted or dashed:
+    # When gridlines are dotted or dashed:
     grid <- theme.pars$panel.grid
     grid.major <- theme.pars$panel.grid.major
     if ((!is.null(grid$linetype) || !is.null(grid.major$linetype)) && 
@@ -292,9 +292,9 @@ gg2list <- function(p){
     }
     
     ax.list$showgrid <- !is.blank(s("panel.grid.major.%s"))
-    ## These numeric length variables are not easily convertible.
-    ##ax.list$gridwidth <- as.numeric(theme.pars$panel.grid.major$size)
-    ##ax.list$ticklen <- as.numeric(theme.pars$axis.ticks.length)
+    # These numeric length variables are not easily convertible.
+    #ax.list$gridwidth <- as.numeric(theme.pars$panel.grid.major$size)
+    #ax.list$ticklen <- as.numeric(theme.pars$axis.ticks.length)
     
     theme2font <- function(text){
       if(!is.null(text)){
@@ -320,7 +320,7 @@ gg2list <- function(p){
     }
     ax.list$tickfont <- theme2font(tick.text)
     
-    ## Translate axes labels.
+    # Translate axes labels.
     scale.i <- which(p$scales$find(xy))
     ax.list$title <- if(length(scale.i)){
       sc <- p$scales$scales[[scale.i]]
@@ -349,16 +349,16 @@ gg2list <- function(p){
     ax.list$showline <- !is.blank("panel.border", TRUE)
     ax.list$linecolor <- toRGB(theme.pars$panel.border$colour)
     ax.list$linewidth <- theme.pars$panel.border$size
-    ## Some other params that we used in animint but we don't yet
-    ## translate to plotly:
+    # Some other params that we used in animint but we don't yet
+    # translate to plotly:
     !is.blank(s("axis.line.%s"))
     layout[[s("%saxis")]] <- ax.list
   }
-  ## copy [x/y]axis to [x/y]axisN and set domain, range, etc. for each
+  # copy [x/y]axis to [x/y]axisN and set domain, range, etc. for each
   xaxis.title <- layout$xaxis$title
   yaxis.title <- layout$yaxis$title
-  inner.margin <- 0.01 ## between facets
-  outer.margin <- 0.05 ## to put titles outside of the plots
+  inner.margin <- 0.01 # between facets
+  outer.margin <- 0.05 # to put titles outside of the plots
   orig.xaxis <- layout$xaxis
   orig.yaxis <- layout$yaxis
   if (nrow(gglayout) > 1) {
@@ -428,7 +428,7 @@ gg2list <- function(p){
         layout[[yaxis.name]]$autorange <- FALSE
       }
     }
-    ## add panel titles as annotations
+    # add panel titles as annotations
     annotations <- list()
     nann <- 1
     make.label <- function(text, x, y, xanchor="auto", yanchor="auto", textangle=0)
@@ -472,8 +472,8 @@ gg2list <- function(p){
         }
       }
       
-      ## add empty traces everywhere so that the background shows even if there
-      ## is no data for a facet
+      # add empty traces everywhere so that the background shows even if there
+      # is no data for a facet
       for (r in seq_len(max(gglayout$ROW)))
         for (c in seq_len(max(gglayout$COL)))
           trace.list <- c(trace.list, list(list(xaxis=paste0("x", c), yaxis=paste0("y", r), showlegend=FALSE)))
@@ -498,7 +498,7 @@ gg2list <- function(p){
       }
     }
     
-    ## axes titles
+    # axes titles
     annotations[[nann]] <- make.label(xaxis.title, 
                                       0.5, 
                                       -outer.margin,
@@ -512,17 +512,17 @@ gg2list <- function(p){
     layout$annotations <- annotations
   }
   
-  ## Remove legend if theme has no legend position
+  # Remove legend if theme has no legend position
   layout$showlegend <- !(theme.pars$legend.position=="none")
   
-  ## Main plot title.
+  # Main plot title.
   layout$title <- built$plot$labels$title
   
-  ## Background color.
+  # Background color.
   layout$plot_bgcolor <- toRGB(theme.pars$panel.background$fill)
   layout$paper_bgcolor <- toRGB(theme.pars$plot.background$fill)
   
-  ## Legend.
+  # Legend.
   layout$margin$r <- 10
   if (exists("increase_margin_r")) {
     layout$margin$r <- 60
@@ -563,23 +563,23 @@ gg2list <- function(p){
     layout$annotations <- annotations
   }
   
-  ## Family font for text
+  # Family font for text
   if (!is.null(theme.pars$text$family)) {
     layout$titlefont$family   <- theme.pars$text$family
     layout$legend$font$family <- theme.pars$text$family
   }
   
-  ## Family font for title
+  # Family font for title
   if (!is.null(theme.pars$plot.title$family)) {
     layout$titlefont$family <- theme.pars$plot.title$family
   }
   
-  ## Family font for legend
+  # Family font for legend
   if (!is.null(theme.pars$legend.text$family)) {
     layout$legend$font$family <- theme.pars$legend.text$family
   }
   
-  ## Bold, italic and bold.italic face for text
+  # Bold, italic and bold.italic face for text
   text_face <- theme.pars$text$face
   if (!is.null(text_face)) {
     if (text_face=="bold") {
@@ -597,7 +597,7 @@ gg2list <- function(p){
     }
   }
   
-  ## Bold, italic and bold.italic face for title
+  # Bold, italic and bold.italic face for title
   title_face <- theme.pars$plot.title$face
   if (!is.null(title_face)) {
     if (title_face=="bold") {
@@ -609,7 +609,7 @@ gg2list <- function(p){
     }
   }
   
-  ## Bold, italic, and bold.italic face for axis title
+  # Bold, italic, and bold.italic face for axis title
   title_face <- list(theme.pars$axis.title.y$face,
                      theme.pars$axis.title.x$face)
   sub_elem <- c("yaxis", "xaxis")

From 1d4c09ae8ebef5a1365112a032e7a60b1178e3db Mon Sep 17 00:00:00 2001
From: "Anonymous@example.com" <cpsievert1@gmail.com>
Date: Fri, 20 Feb 2015 14:22:48 -0600
Subject: [PATCH 6/9] rename facet test and save results

---
 tests/testthat/{test-facets.R => test-ggplot-facets.R} | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)
 rename tests/testthat/{test-facets.R => test-ggplot-facets.R} (91%)

diff --git a/tests/testthat/test-facets.R b/tests/testthat/test-ggplot-facets.R
similarity index 91%
rename from tests/testthat/test-facets.R
rename to tests/testthat/test-ggplot-facets.R
index a52348bff9..e39575f853 100644
--- a/tests/testthat/test-facets.R
+++ b/tests/testthat/test-ggplot-facets.R
@@ -51,7 +51,6 @@ test_that("3 facets becomes 3 panels", {
 # expect a certain number of _unique_ [x/y] axes
 expect_axes <- function(info, n, axis = "x") {
   pattern <- paste0("^", axis, "axis([0-9]+)?$")
-  #n.axes <- length(grep(pattern, names(info$kwargs$layout)))
   axes <- with(info$kwargs, layout[grepl(pattern, names(layout))])
   n.axes <- length(axes)
   ranges <- do.call("rbind", lapply(axes, function(x) x$range))
@@ -62,16 +61,19 @@ no_panels <- ggplot(mtcars, aes(mpg, wt)) + geom_point()
 
 test_that("facet_wrap(..., scales = 'free') creates interior scales", {
   free_both <- no_panels + facet_wrap(~am+vs, scales = "free")
+  save_outputs(free_both, "facet_wrap_free")
   info <- gg2list(free_both)
   expect_axes(info, 4L)
   expect_axes(info, 4L, "y")
   
   free_y <- no_panels + facet_wrap(~am+vs, scales = "free_y")
+  save_outputs(free_y, "facet_wrap_free_y")
   info <- gg2list(free_y)
   expect_axes(info, 1L)
   expect_axes(info, 4L, "y")
   
   free_x <- no_panels + facet_wrap(~am+vs, scales = "free_x")
+  save_outputs(free_x, "facet_wrap_free_x")
   info <- gg2list(free_x)
   expect_axes(info, 4L)
   expect_axes(info, 1L, "y")
@@ -79,17 +81,20 @@ test_that("facet_wrap(..., scales = 'free') creates interior scales", {
 
 test_that("facet_grid(..., scales = 'free') doesnt create interior scales.", {
   free_both <- no_panels + facet_grid(vs~am, scales = "free")
+  save_outputs(free_both, "facet_grid_free")
   info <- gg2list(free_both)
   expect_axes(info, 2L)
   expect_axes(info, 2L, "y")
   
   free_y <- no_panels + facet_grid(vs~am, scales = "free_y")
+  save_outputs(free_y, "facet_grid_free_y")
   info <- gg2list(free_y)
   expect_axes(info, 1L)
   expect_axes(info, 2L, "y")
   
   free_x <- no_panels + facet_grid(vs~am, scales = "free_x")
+  save_outputs(free_x, "facet_grid_free_x")
   info <- gg2list(free_x)
   expect_axes(info, 2L)
   expect_axes(info, 1L, "y")
-})
\ No newline at end of file
+})

From 05d29e763e3984501535998de9a1aef21d86902f Mon Sep 17 00:00:00 2001
From: "Anonymous@example.com" <cpsievert1@gmail.com>
Date: Thu, 26 Feb 2015 21:06:21 -0500
Subject: [PATCH 7/9] fix attempt

---
 R/ggplotly.R | 148 ++++++++++++++++++++++++---------------------------
 1 file changed, 71 insertions(+), 77 deletions(-)

diff --git a/R/ggplotly.R b/R/ggplotly.R
index 0a86305d03..b041315746 100644
--- a/R/ggplotly.R
+++ b/R/ggplotly.R
@@ -396,17 +396,14 @@ gg2list <- function(p){
       y <- row * row.size
       ymin <- y - row.size
       ymax <- y - inner.margin
-      xaxis.name <- if (panel == 1) "xaxis" else paste0("xaxis", panel)
-      yaxis.name <- if (panel == 1) "yaxis" else paste0("yaxis", panel)
-      #layout defaults that won't change depending on the type of facet
-      layout[[xaxis.name]] <- orig.xaxis
-      layout[[xaxis.name]]$domain <- c(xmin, xmax)
-      layout[[xaxis.name]]$title <- NULL
-      layout[[yaxis.name]] <- orig.yaxis
-      layout[[yaxis.name]]$domain <- c(ymin, ymax)
-      layout[[yaxis.name]]$title <- NULL
-      # facet_wrap allows for a different x/y axis on each panel
+      # assume grid layout by default where axes are restrict to the exterior
+      xaxis.name <- if (col == 1) "xaxis" else paste0("xaxis", col)
+      yaxis.name <- if (row == 1) "yaxis" else paste0("yaxis", row)
+      # anchor needs to be incremented if the corresponding axis is "free"
+      xanchor <- "y"
+      yanchor <- "x"
       if ("wrap" %in% class(p$facet)) {
+        # in wrap layout, axes can be drawn on interior (if scales are free)
         # make room for facet strip label
         ymax <- ymax - 0.04
         # make room for yaxis labels (this should be a function of label size)
@@ -422,19 +419,26 @@ gg2list <- function(p){
           ymin <- ymin + 0.02
         }
         if (p$facet$free$y && panel > 1) {
-          # is it safe to assume npanels == ntraces?
+          # draw a y-axis on each panel
           yaxis.name <- paste0("yaxis", panel)
           trace.list[[i]]$yaxis <- paste0("y", panel)
-          layout[[yaxis.name]]$anchor <- paste0("x", panel)
-        }
+          yanchor <- if (p$facet$free$x) paste0("x", panel) else paste0("x",col)
+        } 
         if (p$facet$free$x && panel > 1) {
+          # draw an x-axis on each panel
           xaxis.name <- paste0("xaxis", panel)
           trace.list[[i]]$xaxis <- paste0("x", panel)
-          layout[[xaxis.name]]$anchor <- paste0("y", panel)
+          xanchor <- if (p$facet$free$y) paste0("y", panel) else paste0("y",row)
         }
-        layout[[xaxis.name]]$domain <- c(xmin, xmax)
-        layout[[yaxis.name]]$domain <- c(ymin, ymax)
-      }
+      } 
+      layout[[xaxis.name]] <- orig.xaxis
+      layout[[xaxis.name]]$domain <- c(xmin, xmax)
+      layout[[xaxis.name]]$anchor <- xanchor
+      layout[[xaxis.name]]$title <- NULL
+      layout[[yaxis.name]] <- orig.yaxis
+      layout[[yaxis.name]]$domain <- c(ymin, ymax)
+      layout[[yaxis.name]]$anchor <- yanchor
+      layout[[yaxis.name]]$title <- NULL
       if (is.null(layout[[xaxis.name]]$anchor)) 
         layout[[xaxis.name]]$anchor <- "y"
       if (is.null(layout[[yaxis.name]]$anchor)) 
@@ -456,70 +460,60 @@ gg2list <- function(p){
       list(text=text, showarrow=FALSE, x=x, y=y, ax=0, ay=0, 
            xref="paper", yref="paper", xanchor=xanchor, yanchor=yanchor, 
            textangle=textangle)
-    
     if ("grid" %in% class(p$facet)) {
       frows <- names(p$facet$rows)
       nann <- 1
-      make.label <- function(text, x, y, xanchor="auto", yanchor="auto", textangle=0)
-        list(text=text, showarrow=FALSE, x=x, y=y, ax=0, ay=0, 
-             xref="paper", yref="paper", xanchor=xanchor, yanchor=yanchor, 
-             textangle=textangle)
       
-      if ("grid" %in% class(p$facet)) {
-        frows <- names(p$facet$rows)
-        nann <- 1
-        
-        for (i in seq_len(max(gglayout$ROW))) {
-          text <- paste(lapply(gglayout[gglayout$ROW == i, frows, drop=FALSE][1,],
-                               as.character),
-                        collapse=", ")
-          if (text != "") {  # to not create extra annotations
-            increase_margin_r <- TRUE
-            annotations[[nann]] <- make.label(text,
-                                              1 + outer.margin - 0.04,
-                                              row.size * (max(gglayout$ROW)-i+0.5),
-                                              xanchor="center",
-                                              textangle=90)
-            nann <- nann + 1
-          }
-        }
-        fcols <- names(p$facet$cols)
-        for (i in seq_len(max(gglayout$COL))) {
-          text <- paste(lapply(gglayout[gglayout$COL == i, fcols, drop=FALSE][1,],
-                               as.character),
-                        collapse=", ")
-          if (text!="") {
-            annotations[[nann]] <- make.label(text,
-                                              col.size * (i-0.5) - inner.margin/2,
-                                              1 + outer.margin,
-                                              xanchor="center")
-            nann <- nann + 1
-          }
-        }
-        
-        # add empty traces everywhere so that the background shows even if there
-        # is no data for a facet
-        for (r in seq_len(max(gglayout$ROW)))
-          for (c in seq_len(max(gglayout$COL)))
-            trace.list <- c(trace.list, list(list(xaxis=paste0("x", c), yaxis=paste0("y", r), showlegend=FALSE)))
-      } else if ("wrap" %in% class(p$facet)) {
-        facets <- names(p$facet$facets)
-        for (i in seq_len(max(as.numeric(gglayout$PANEL)))) {
-          ix <- gglayout$PANEL == i
-          row <- gglayout$ROW[ix]
-          col <- gglayout$COL[ix]
-          text <- paste(lapply(gglayout[ix, facets, drop=FALSE][1,],
-                               as.character),
-                        collapse=", ")
-          annotations[[nann]] <- make.label(text, 
-                                            col.size * (col-0.5) - inner.margin/2,
-                                            row.size * (max(gglayout$ROW) - row + 0.985),
+      for (i in seq_len(max(gglayout$ROW))) {
+        text <- paste(lapply(gglayout[gglayout$ROW == i, frows, drop=FALSE][1,],
+                             as.character),
+                      collapse=", ")
+        if (text != "") {  # to not create extra annotations
+          increase_margin_r <- TRUE
+          annotations[[nann]] <- make.label(text,
+                                            1 + outer.margin - 0.04,
+                                            row.size * (max(gglayout$ROW)-i+0.5),
                                             xanchor="center",
-                                            yanchor="top")
+                                            textangle=90)
+          nann <- nann + 1
+        }
+      }
+      fcols <- names(p$facet$cols)
+      for (i in seq_len(max(gglayout$COL))) {
+        text <- paste(lapply(gglayout[gglayout$COL == i, fcols, drop=FALSE][1,],
+                             as.character),
+                      collapse=", ")
+        if (text!="") {
+          annotations[[nann]] <- make.label(text,
+                                            col.size * (i-0.5) - inner.margin/2,
+                                            1 + outer.margin,
+                                            xanchor="center")
           nann <- nann + 1
         }
       }
       
+      # add empty traces everywhere so that the background shows even if there
+      # is no data for a facet
+      for (r in seq_len(max(gglayout$ROW)))
+        for (c in seq_len(max(gglayout$COL)))
+          trace.list <- c(trace.list, list(list(xaxis=paste0("x", c), yaxis=paste0("y", r), showlegend=FALSE)))
+    } else if ("wrap" %in% class(p$facet)) {
+      facets <- names(p$facet$facets)
+      for (i in seq_len(max(as.numeric(gglayout$PANEL)))) {
+        ix <- gglayout$PANEL == i
+        row <- gglayout$ROW[ix]
+        col <- gglayout$COL[ix]
+        text <- paste(lapply(gglayout[ix, facets, drop=FALSE][1,],
+                             as.character),
+                      collapse=", ")
+        annotations[[nann]] <- make.label(text, 
+                                          col.size * (col-0.5) - inner.margin/2,
+                                          row.size * (max(gglayout$ROW) - row + 0.985),
+                                          xanchor="center",
+                                          yanchor="top")
+        nann <- nann + 1
+      }
+      
       # axes titles
       annotations[[nann]] <- make.label(xaxis.title, 
                                         0.5, 
@@ -553,7 +547,6 @@ gg2list <- function(p){
   layout$legend <- list(bordercolor="transparent", 
                         x=1.05, y=1/2,
                         xanchor="center", yanchor="top")
-  
   # Workaround for removing unnecessary legends.
   # [markUnique != "x"] is for boxplot's particular case.
   if (any(names(layer.aes) %in% markUnique[markUnique != "x"]) == FALSE)
@@ -687,10 +680,11 @@ gg2list <- function(p){
     for(other.i in seq_along(not.merged)){
       other <- not.merged[[other.i]]
       criteria <- c()
-      for(must.be.equal in c("x", "y", "xaxis", "yaxis")){
-        other.attr <- other[[must.be.equal]]
-        tr.attr <- tr[[must.be.equal]]
-        criteria[[must.be.equal]] <- isTRUE(all.equal(other.attr, tr.attr))
+      must.be.equal <- c("x", "y", "xaxis", "yaxis")
+      for(j in must.be.equal){
+        other.attr <- other[[j]]
+        tr.attr <- tr[[j]]
+        criteria[[j]] <- isTRUE(all.equal(other.attr, tr.attr))
       }
       if(all(criteria)){
         can.merge[[other.i]] <- TRUE

From f800c1249fc36072c1b4dd775b88659144b80f17 Mon Sep 17 00:00:00 2001
From: cpsievert <cpsievert1@gmail.com>
Date: Mon, 9 Mar 2015 16:58:16 -0500
Subject: [PATCH 8/9] Fix annotations for facet_grid()

---
 R/ggplotly.R | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/R/ggplotly.R b/R/ggplotly.R
index c0231e2e20..bab347df2d 100644
--- a/R/ggplotly.R
+++ b/R/ggplotly.R
@@ -527,7 +527,7 @@ gg2list <- function(p){
                                           yanchor="top")
         nann <- nann + 1
       }
-      
+    }
       # axes titles
       annotations[[nann]] <- make.label(xaxis.title, 
                                         0.5, 
@@ -538,9 +538,7 @@ gg2list <- function(p){
                                         -outer.margin, 
                                         0.5,
                                         textangle=-90)
-      
-      layout$annotations <- annotations
-    }
+    layout$annotations <- annotations
   }
   
   # Remove legend if theme has no legend position

From f9e252af61276fd40168fc8916024877c7f2f6a3 Mon Sep 17 00:00:00 2001
From: cpsievert <cpsievert1@gmail.com>
Date: Tue, 10 Mar 2015 11:58:20 -0500
Subject: [PATCH 9/9] Bump version, update NEWS, add Carson as author

---
 DESCRIPTION | 6 ++++--
 NEWS        | 4 ++++
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/DESCRIPTION b/DESCRIPTION
index cfdb548b5c..2bffaf5d01 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -1,7 +1,7 @@
 Package: plotly
 Type: Package
 Title: Interactive, publication-quality graphs online.
-Version: 0.5.22
+Version: 0.5.23
 Authors@R: c(person("Chris", "Parmer", role = c("aut", "cre"),
     email = "chris@plot.ly"),
     person("Scott", "Chamberlain", role = "aut",
@@ -13,7 +13,9 @@ Authors@R: c(person("Chris", "Parmer", role = c("aut", "cre"),
     person("Marianne", "Corvellec", role="aut",
     email="marianne@plot.ly"),
     person("Pedro", "Despouy", role="aut",
-    email="pedro@plot.ly"))
+    email="pedro@plot.ly"),
+    person("Carson", "Sievert", role="aut",
+    email="cpsievert1@gmail.com"))
 Author: Chris Parmer
 Maintainer: Marianne Corvellec <marianne@plot.ly>
 License: MIT + file LICENSE
diff --git a/NEWS b/NEWS
index 21360e5661..9f48910e6d 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,7 @@
+0.5.23 -- 10 March 2015.
+
+Implemented #167
+
 0.5.22 -- 2 March 2015.
 
 Fixes for ylim() #171.