[med-svn] [r-cran-scatterd3] 01/09: Imported Upstream version 0.6.2+dfsg

Andreas Tille tille at debian.org
Sat Jul 2 10:24:34 UTC 2016


This is an automated email from the git hooks/post-receive script.

tille pushed a commit to branch master
in repository r-cran-scatterd3.

commit e0973f30669862f13b4712ed6967d5acae64e00e
Author: Andreas Tille <tille at debian.org>
Date:   Sat Jul 2 11:36:06 2016 +0200

    Imported Upstream version 0.6.2+dfsg
---
 DESCRIPTION                                    |   54 ++
 MD5                                            |   23 +
 NAMESPACE                                      |   10 +
 NEWS                                           |   72 ++
 R/scatterD3-Rd.R                               |   19 +
 R/scatterD3.R                                  |  248 +++++
 README.md                                      |   74 ++
 build/vignette.rds                             |  Bin 0 -> 201 bytes
 inst/doc/introduction.R                        |   49 +
 inst/doc/introduction.Rmd                      |  177 ++++
 inst/doc/introduction.html                     |  228 +++++
 inst/htmlwidgets/lib/d3-lasso-plugin/LICENSE   |   28 +
 inst/htmlwidgets/lib/d3-lasso-plugin/README.md |   88 ++
 inst/htmlwidgets/lib/d3-lasso-plugin/lasso.css |   45 +
 inst/htmlwidgets/lib/d3-lasso-plugin/lasso.js  |  370 +++++++
 inst/htmlwidgets/lib/scatterD3.css             |   29 +
 inst/htmlwidgets/scatterD3.js                  | 1238 ++++++++++++++++++++++++
 inst/htmlwidgets/scatterD3.yaml                |   13 +
 man/scatterD3-shiny.Rd                         |   31 +
 man/scatterD3.Rd                               |  118 +++
 vignettes/introduction.Rmd                     |  177 ++++
 21 files changed, 3091 insertions(+)

diff --git a/DESCRIPTION b/DESCRIPTION
new file mode 100644
index 0000000..8cef2ed
--- /dev/null
+++ b/DESCRIPTION
@@ -0,0 +1,54 @@
+Package: scatterD3
+Type: Package
+Title: D3 JavaScript Scatterplot from R
+Version: 0.6.2
+Date: 2016-03-24
+Authors at R: c(
+    person(
+      "Julien", "Barnier",
+      email = "julien.barnier at ens-lyon.fr",
+      role = c("aut", "cre")
+    ),
+    person(
+        "Kent", "Russell"
+        , role = c("aut", "ctb")
+        , email = "kent.russell at timelyportfolio.com"
+    ),
+    person(
+        "Mike", "Bostock"
+        , role = c("aut", "cph")
+        , comment = "d3.js library, http://d3js.org"
+    ),
+    person(
+        "Susie", "Lu"
+        , role = c("aut", "cph")
+        , comment = "d3-legend library, http://d3-legend.susielu.com/"
+    ),
+    person(
+        "Speros", "Kokenes"
+        , role = c("aut", "cph")
+        , comment = "d3-lasso-plugin library, https://github.com/skokenes/D3-Lasso-Plugin"
+    )
+  )
+Maintainer: Julien Barnier <julien.barnier at ens-lyon.fr>
+Description: Creates 'D3' 'JavaScript' scatterplots from 'R' with interactive
+    features : panning, zooming, tooltips, etc.
+License: GPL (>= 3)
+VignetteBuilder: knitr
+URL: https://github.com/juba/scatterD3
+BugReports: https://github.com/juba/scatterD3/issues
+LazyData: TRUE
+Enhances: shiny
+Imports: htmlwidgets, digest, ellipse
+Suggests: knitr, rmarkdown
+RoxygenNote: 5.0.1
+NeedsCompilation: no
+Packaged: 2016-03-24 13:27:43 UTC; julien
+Author: Julien Barnier [aut, cre],
+  Kent Russell [aut, ctb],
+  Mike Bostock [aut, cph] (d3.js library, http://d3js.org),
+  Susie Lu [aut, cph] (d3-legend library, http://d3-legend.susielu.com/),
+  Speros Kokenes [aut, cph] (d3-lasso-plugin library,
+    https://github.com/skokenes/D3-Lasso-Plugin)
+Repository: CRAN
+Date/Publication: 2016-03-24 23:53:34
diff --git a/MD5 b/MD5
new file mode 100644
index 0000000..2f0d2ef
--- /dev/null
+++ b/MD5
@@ -0,0 +1,23 @@
+4eb9c371af7bdea6941fe915b5619e7f *DESCRIPTION
+6175be66a588675cfcc631efac40bccb *NAMESPACE
+fb53364a6e90e8b83fe6035ef6b6e44d *NEWS
+cceffe7465fc66364cdfb8c78319bcb7 *R/scatterD3-Rd.R
+c0bf7b8873d49b0336368e68ca5f4097 *R/scatterD3.R
+29908a3564f2d24132dd533219e2e9bf *README.md
+bffb60ce3b41d716319d5e3909b064ab *build/vignette.rds
+3d34bf1d499c53db86a66766defdf517 *inst/doc/introduction.R
+c286a29f2adff2645c64d844b6cba45b *inst/doc/introduction.Rmd
+9171e7d9d3f5c1175c231c3a42aede80 *inst/doc/introduction.html
+9a43e3ae9eeb6821b99b41158d0b08cd *inst/htmlwidgets/lib/LICENSE
+e9b501452b74836764e9c186f2ed337e *inst/htmlwidgets/lib/d3-3.5.6.min.js
+59827795b5ab14a5edc7e6bf8744cef7 *inst/htmlwidgets/lib/d3-lasso-plugin/LICENSE
+95f5a84cb5c61c5abb84da94f8c87b2c *inst/htmlwidgets/lib/d3-lasso-plugin/README.md
+ef54456443f8077b51e73565ef195f96 *inst/htmlwidgets/lib/d3-lasso-plugin/lasso.css
+53a84687b07856608ed1d57fc5adb6e1 *inst/htmlwidgets/lib/d3-lasso-plugin/lasso.js
+1fee5abd329e5719a45e7aca380e80c6 *inst/htmlwidgets/lib/d3-legend.min.js
+210c678aef0965f051785069e82700ca *inst/htmlwidgets/lib/scatterD3.css
+d6704d4f9ce37bf03d163ad3b052e2f3 *inst/htmlwidgets/scatterD3.js
+221e87f55ec78ce2a5e7b048de150287 *inst/htmlwidgets/scatterD3.yaml
+12229f19d7fb5d246373d4a3e633534b *man/scatterD3-shiny.Rd
+1bb1c38f66153ee3f513b5ac933ec8e9 *man/scatterD3.Rd
+c286a29f2adff2645c64d844b6cba45b *vignettes/introduction.Rmd
diff --git a/NAMESPACE b/NAMESPACE
new file mode 100644
index 0000000..01e8fa9
--- /dev/null
+++ b/NAMESPACE
@@ -0,0 +1,10 @@
+# Generated by roxygen2: do not edit by hand
+
+export(renderScatterD3)
+export(scatterD3)
+export(scatterD3Output)
+importFrom(ellipse,ellipse)
+importFrom(htmlwidgets,JS)
+importFrom(htmlwidgets,shinyRenderWidget)
+importFrom(htmlwidgets,shinyWidgetOutput)
+importFrom(stats,cov)
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..d3ede41
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,72 @@
+scatterD3 0.6.2
+------------------------------------------------------
+
+* Darker points color during lasso selection
+* Bugfix : Remove any previous anchor from clip-path urls
+* Bugfix : deal with NA in size_var
+
+scatterD3 0.6.1
+------------------------------------------------------
+
+* New feature : d3 lasso plugin integration, initial work by @timelyportfolio
+* New feature : allow to draw confidence ellipses for all points or for col_var groups
+* Bugfix : underscores appearing instead of spaces in legend text (thanks @TimBock)
+* Bugfix : xlim and ylim not taken ignored when fixed = TRUE (thanks @TimBock)
+* Bugfix : legend must not be displayed when legend_width = 0 and fixed = TRUE (thanks @TimBock)
+* Bugfix : wrong legend hover highlighting when the label is "0"
+* Bugfix : convert NA to "NA" in color and symbol mapping variables
+
+
+scatterD3 0.5.1
+------------------------------------------------------
+
+* Fix zoom reset when several charts in the same shiny app
+* Switch to 20 colors ordinary scales if there are more than 10 color variable levels
+* Bugfix : wrong svg reference passed to hover legend functions when several scatterD3 instances in the same shiny app
+* Make point labels updatable
+* Manage changes of unit_circle settings
+
+
+scatterD3 0.5
+------------------------------------------------------
+
+* Bugfixes for when several scatterD3 instances are in the same shiny app
+* Fix `fixed` 1:1 aspect ratio not working as intended
+* New `unit_circle` argument to draw a unit circle around origin
+* Text labels are placed below the corresponding arrow by default when y < 0
+* New `type_var` argument, which allows to selectively draw arrows (starting from origin) instead of points
+
+
+scatterD3 0.4
+------------------------------------------------------
+
+* A temporary line is drawn between text and point when dragging a label
+* Fix plots updating when several widgets are in the same shiny app
+* Fix tooltips not showing in Firefox
+* Fix clip-path URL problems when exporting to SVG
+
+
+scatterD3 0.3
+------------------------------------------------------
+
+* Charts integrated into a shiny app are now fully updatable : when data or settings change, the plot is updated with smooth transitions instead of being redrawn. See the `transitions` and `key_var` arguments.
+* HTML DOM id of elements linked to the "Reset zoom" and "Export to SVG" features can now be given as arguments.
+
+
+scatterD3 0.2
+------------------------------------------------------
+
+* Add `colors` argument to specify a custom set of point colors. A named vector can be used to directly map values to colors (Thanks @timelyportfolio)
+* Complete code reorganisation, much cleaner
+* New `legend_width` argument
+* Use `d3-legend` plugin for legend generation (http://d3-legend.susielu.com/)
+* New `xlim` and `ylim` arguments for manual axis limits specification (Thanks @tinyheero)
+* Fix tooltips not showing under Shiny/Bootstrap (Thanks @tinyheero for reporting)
+* Fix tooltip content when several scatter plots are displayed on the same page
+* Respect custom label position when zooming
+
+
+scatterD3 0.1.1
+------------------------------------------------------
+
+* First version
diff --git a/R/scatterD3-Rd.R b/R/scatterD3-Rd.R
new file mode 100644
index 0000000..d544502
--- /dev/null
+++ b/R/scatterD3-Rd.R
@@ -0,0 +1,19 @@
+#' Shiny bindings for scatterD3 widgets
+#'
+#' Output and render functions for using scatterD3 widgets within Shiny
+#' applications and interactive Rmd documents.
+#'
+#' @param outputId output variable to read from
+#' @param width,height Must be a valid CSS unit (like \code{"100\%"},
+#'   \code{"400px"}, \code{"auto"}) or a number, which will be coerced to a
+#'   string and have \code{"px"} appended.
+#' @param expr An expression that generates a scatterD3 scatter plot.
+#' @param env The environment in which to evaluate \code{expr}.
+#' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This
+#'   is useful if you want to save an expression in a variable.
+#'
+#' @importFrom htmlwidgets shinyWidgetOutput
+#' @importFrom htmlwidgets shinyRenderWidget
+#'
+#' @name scatterD3-shiny
+NULL
diff --git a/R/scatterD3.R b/R/scatterD3.R
new file mode 100644
index 0000000..e37bfec
--- /dev/null
+++ b/R/scatterD3.R
@@ -0,0 +1,248 @@
+#' Scatter plot HTML widget
+#'
+#' Interactive scatter plots based on htmlwidgets and d3.js
+#'
+#' @param x numerical vector of x values
+#' @param y numerical vector of y values
+#' @param lab optional character vector of text labels
+#' @param point_size points size. Ignored if size_var is not NULL.
+#' @param labels_size text labels size
+#' @param point_opacity points opacity, as an integer (same opacity for all points) or a vector of integers
+#' @param fixed force a 1:1 aspect ratio
+#' @param col_var optional vector for points color mapping
+#' @param colors vector of custom points colors. Colors must be
+#'          defined as an hexadecimal string (eg "#FF0000").  If
+#'          \code{colors} is a named list or vector, then the colors will
+#'          be associated with their name within \code{col_var}.
+#' @param ellipses draw confidence ellipses for points or the different color mapping groups
+#' @param ellipses_level confidence level for ellipses (0.95 by default)
+#' @param symbol_var optional vector for points symbol mapping
+#' @param size_var optional vector for points size mapping
+#' @param size_range numeric vector of length 2, giving the minimum and maximum point sizes when mapping with size_var
+#' @param col_lab color legend title
+#' @param symbol_lab symbols legend title
+#' @param size_lab size legend title
+#' @param key_var optional vector of rows ids. This is passed as a key to d3, and is only added in shiny apps where displayed rows are filtered interactively.
+#' @param type_var optional vector of points type : "point" for adot (default), "arrow" for an arrow starting from the origin.
+#' @param unit_circle set tot TRUE to draw a unit circle
+#' @param tooltips logical value to display tooltips when hovering points
+#' @param tooltip_text optional character vector of tooltips text
+#' @param xlab x axis label
+#' @param ylab y axis label
+#' @param xlim numeric vector of length 2, manual x axis limits
+#' @param ylim numeric vector of length 2, manual y axis limits
+#' @param lasso logical value to add {https://github.com/skokenes/D3-Lasso-Plugin}{d3-lasso-plugin} feature
+#' @param lasso_callback the body of a JavaScript callback function with the argument \code{sel} to be applied to a lasso plugin selection
+#' @param html_id manually specify an HTML id for the svg root node. A random one is generated by default.
+#' @param dom_id_reset_zoom HTML DOM id of the element to bind the "reset zoom" control to.
+#' @param dom_id_svg_export HTML DOM id of the element to bind the "svg export" control to.
+#' @param dom_id_lasso_toggle HTML DOM id of the element to bind the "toggle lasso" control to.
+#' @param transitions if TRUE, data updates are displayed with smooth transitions, if FALSE the whole chart is redrawn. Only used within shiny apps.
+#' @param legend_width legend area width, in pixels. Set to 0 to disable legend completely.
+#' @param width figure width, computed when displayed
+#' @param height figure height, computed when displayed
+#'
+#' @description Generates an interactive scatter plot based on d3.js.
+#' Interactive features include zooming, panning, text labels moving, tooltips,
+#' fading effects in legend. Additional handlers are provided to change label
+#' size, point opacity or export the figure as an SVG file via HTML form controls.
+#'
+#' @author Julien Barnier <julien.barnier@@ens-lyon.fr>
+#'
+#' @source
+#' D3.js was created by Michael Bostock. See \url{http://d3js.org/}
+#'
+#' @examples
+#' scatterD3(x = mtcars$wt, y = mtcars$mpg, lab = rownames(mtcars),
+#'           col_var = mtcars$cyl, symbol_var = mtcars$am,
+#'           xlab = "Weight", ylab = "Mpg", col_lab = "Cylinders",
+#'           symbol_lab = "Manual transmission", html_id = NULL)
+#'
+#' @importFrom ellipse ellipse
+#' @importFrom stats cov
+#' @importFrom htmlwidgets JS
+#' @export
+#'
+scatterD3 <- function(x, y, lab = NULL,
+                      point_size = 64, labels_size = 10,
+                      point_opacity = 1,
+                      fixed = FALSE, col_var = NULL,
+                      colors = NULL,
+                      ellipses = FALSE,
+                      ellipses_level = 0.95,
+                      symbol_var = NULL,
+                      size_var = NULL,
+                      size_range = c(10,300),
+                      col_lab = NULL, symbol_lab = NULL,
+                      size_lab = NULL,
+                      key_var = NULL,
+                      type_var = NULL,
+                      unit_circle = FALSE,
+                      tooltips = TRUE,
+                      tooltip_text = NULL,
+                      xlab = NULL, ylab = NULL,
+                      html_id = NULL,
+                      width = NULL, height = NULL,
+                      legend_width = 150,
+                      xlim = NULL, ylim = NULL,
+                      dom_id_reset_zoom = "scatterD3-reset-zoom",
+                      dom_id_svg_export = "scatterD3-svg-export",
+                      dom_id_lasso_toggle = "scatterD3-lasso-toggle",
+                      transitions = FALSE,
+                      lasso = FALSE,
+                      lasso_callback = NULL) {
+
+  ## Variable names as default labels
+  if (is.null(xlab)) xlab <- deparse(substitute(x))
+  if (is.null(ylab)) ylab <- deparse(substitute(y))
+  if (is.null(col_lab)) col_lab <- deparse(substitute(col_var))
+  if (is.null(symbol_lab)) symbol_lab <- deparse(substitute(symbol_var))
+  if (is.null(size_lab)) size_lab <- deparse(substitute(size_var))
+  if (is.null(html_id)) html_id <- paste0("scatterD3-", paste0(sample(LETTERS,8,replace = TRUE),collapse = ""))
+
+  # colors can be named
+  #  we'll need to convert named vector to a named list
+  #  for the JSON conversion
+  if (!is.null(colors) && !is.null(names(colors))) {
+    colors <- as.list(colors)
+    if (!setequal(names(colors), unique(col_var))) warning("Set of colors and col_var values do not match")
+  }
+
+  ## data element
+  data <- data.frame(x = x, y = y)
+  if (!is.null(lab)) data <- cbind(data, lab = lab)
+  if (!is.null(point_opacity)) data <- cbind(data, point_opacity = point_opacity)
+  if (!is.null(col_var)) {
+    col_var <- as.character(col_var)
+    col_var[is.na(col_var)] <- "NA"
+    data <- cbind(data, col_var = col_var)
+  }
+  if (!is.null(symbol_var)) {
+    symbol_var <- as.character(symbol_var)
+    symbol_var[is.na(symbol_var)] <- "NA"
+    data <- cbind(data, symbol_var = symbol_var)
+  }
+  if (!is.null(size_var)) {
+    warning("NA values in size_var. Values set to min(0, size_var)")
+    size_var[is.na(size_var)] <- min(0, size_var, na.rm = TRUE)
+    data <- cbind(data, size_var = size_var)
+  }
+  if (!is.null(type_var)) data <- cbind(data, type_var = type_var)
+  if (!is.null(key_var)) data <- cbind(data, key_var = key_var)
+  else data <- cbind(data, key_var = seq_along(x))
+  if (!is.null(tooltip_text)) data <- cbind(data, tooltip_text = tooltip_text)
+
+  ## Compute confidence ellipses point positions with ellipse::ellipse.default()
+  compute_ellipse <- function(x, y, level = ellipses_level, npoints = 50) {
+    cx <- mean(x)
+    cy <- mean(y)
+    data.frame(ellipse::ellipse(stats::cov(cbind(x,y)), centre = c(cx, cy), level = level, npoints = npoints))
+  }
+
+  ## Compute ellipses points data
+  ellipses_data <- list()
+  if (ellipses) {
+    ## Only one ellipse
+    if (is.null(col_var)) {
+      ell <- compute_ellipse(x, y)
+      ellipses_data <- append(ellipses_data, list(list(level = "_scatterD3_all", data = ell)))
+    } else {
+      ## One ellipse per col_var level
+      for (l in unique(col_var)) {
+        sel <- col_var == l & !is.na(col_var)
+        if (sum(sel) > 2) {
+          tmpx <- x[sel]
+          tmpy <- y[sel]
+          ell <- compute_ellipse(tmpx, tmpy)
+          ellipses_data <- append(ellipses_data, list(list(level = l, data = ell)))
+        }
+      }
+    }
+  }
+
+  ## List of hashes for each data variable, to track which data elements changed
+  ## to apply updates and transitions in shiny app.
+  hashes <- list()
+  if (transitions) {
+    for (var in c("x", "y", "lab", "key_var", "col_var", "symbol_var", "size_var", "ellipses_data", "point_opacity")) {
+      hashes[[var]] <- digest::digest(get(var), algo = "sha256")
+    }
+  }
+
+  # create a list that contains the settings
+  settings <- list(
+    labels_size = labels_size,
+    point_size = point_size,
+    xlab = xlab,
+    ylab = ylab,
+    has_labels = !is.null(lab),
+    col_var = col_var,
+    col_lab = col_lab,
+    colors = colors,
+    ellipses = ellipses,
+    ellipses_data = ellipses_data,
+    symbol_var = symbol_var,
+    symbol_lab = symbol_lab,
+    size_var = size_var,
+    size_range = size_range,
+    size_lab = size_lab,
+    key_var = key_var,
+    type_var = type_var,
+    unit_circle = unit_circle,
+    has_color_var = !is.null(col_var),
+    has_symbol_var = !is.null(symbol_var),
+    has_size_var = !is.null(size_var),
+    has_legend = !is.null(col_var) || !is.null(symbol_var) || !is.null(size_var),
+    has_tooltips = tooltips,
+    tooltip_text = tooltip_text,
+    has_custom_tooltips = !is.null(tooltip_text),
+    fixed = fixed,
+    legend_width = legend_width,
+    html_id = html_id,
+    xlim = xlim,
+    ylim = ylim,
+    lasso = lasso,
+    lasso_callback = htmlwidgets::JS(lasso_callback),
+    dom_id_reset_zoom = dom_id_reset_zoom,
+    dom_id_svg_export = dom_id_svg_export,
+    dom_id_lasso_toggle = dom_id_lasso_toggle,
+    transitions = transitions,
+    hashes = hashes
+  )
+
+  # pass the data and settings using 'x'
+  x <- list(
+    data = data,
+    settings = settings
+  )
+
+  # create widget
+  htmlwidgets::createWidget(
+      name = 'scatterD3',
+      x,
+      width = width,
+      height = height,
+      package = 'scatterD3',
+      sizingPolicy = htmlwidgets::sizingPolicy(
+          browser.fill = TRUE,
+          viewer.fill = TRUE
+      )
+  )
+}
+
+#' @rdname scatterD3-shiny
+#' @export
+scatterD3Output <- function(outputId, width = '100%', height = '600px'){
+  htmlwidgets::shinyWidgetOutput(outputId, 'scatterD3', width, height, package = 'scatterD3')
+}
+
+#' @rdname scatterD3-shiny
+#' @export
+renderScatterD3 <- function(expr, env = parent.frame(), quoted = FALSE) {
+  if (!quoted) { expr <- substitute(expr) } # force quoted
+  htmlwidgets::shinyRenderWidget(expr, scatterD3Output, env, quoted = TRUE)
+}
+
+
+
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..5f00635
--- /dev/null
+++ b/README.md
@@ -0,0 +1,74 @@
+`scatterD3` is an HTML R widget for interactive scatter plots visualization. It is based on the [htmlwidgets](http://www.htmlwidgets.org/) R package and on the [d3.js](http://d3js.org/) javascript 
+library.
+
+![CRAN Downloads](http://cranlogs.r-pkg.org/badges/last-month/scatterD3) 
+[![Travis-CI Build Status](https://travis-ci.org/juba/scatterD3.svg?branch=master)](https://travis-ci.org/juba/scatterD3)
+[![CRAN_Status_Badge](http://www.r-pkg.org/badges/version/scatterD3)](http://cran.r-project.org/package=scatterD3)
+
+## Features
+
+`scatterD3` currently provides the following features :
+
+- Display points and text labels
+- Possibility to map color, symbol and size with other variables (automatic legend)
+- Zoom with mouse wheel, pan with mouse while zoomed in
+- Ability to drag and move text labels
+- Customizable tooltips when hovering points
+- Points highlighting when hovering legend items
+- Option to draw confidence ellipses around group of points
+- Charts integrated inside a Shiny app are fully updatable with smooth transitions when settings or data change
+- Lasso selection tool integration via d3-lasso-plugin for points highlighting
+
+
+Here is a small preview of what you will get :
+
+![example](https://raw.github.com/juba/scatterD3/master/resources/scatterD3.gif) 
+
+You can also test it live with the [sample shiny app](http://data.nozav.org/app/scatterD3/).
+
+
+## Installation
+
+Install latest stable release from CRAN :
+
+    install.packages("scatterD3")
+
+Or from Github for the latest, bleeding edge, full of bugs version :
+
+    devtools::install_github("juba/scatterD3")
+    
+## Usage
+
+Quick example of the `scatterD3`  function based on the `mtcars` dataset :
+
+```R
+scatterD3(x = mtcars$wt, y = mtcars$mpg, lab = rownames(mtcars),
+          col_var=mtcars$cyl, symbol_var = mtcars$am,
+          xlab = "Weight", ylab = "Mpg", col_lab = "Cylinders",
+          symbol_lab = "Manual transmission")
+```
+              
+See [the introduction vignette](https://rawgit.com/juba/scatterD3/master/vignettes%2Fintroduction.html) for a step-by-step guide and details about the different function arguments.
+
+## Shiny integration
+
+Like every R HTML widget, shiny integration is straightforward. But as a D3 widget, `scatterD3` is *updatable* : changes in settings or data can be displayed via smooth transitions instead of a complete chart redraw, which can provide interesting visual clues.
+
+Furthermore, `scatterD3` provides some additional handlers to two interactive features : SVG export and zoom resetting.
+
+The
+[sample scatterD3 shiny app](http://data.nozav.org/app/scatterD3/) allows you to see the different features described here. You can [check its source code on GitHub](https://github.com/juba/scatterD3_shiny_app) and the [introduction vignette](https://rawgit.com/juba/scatterD3/master/vignettes%2Fintroduction.html) for a better understanding of the different arguments.
+
+
+## Credits
+
+This package has been made possible by :
+
+- Michael Bostock's incredible [d3.js](https://d3js.org/) library and documentation
+- RStudio's [shiny](http://shiny.rstudio.com/) and [htmlwidgets](http://www.htmlwidgets.org/) packages
+- Susie Lu's [d3-legend](https://github.com/susielu/d3-legend) module
+- Rob Moore's [article on reusable d3.js charts](http://www.toptal.com/d3-js/towards-reusable-d3-js-charts)
+- Speros Kokenes' [d3 lasso plugin](https://github.com/skokenes/D3-Lasso-Plugin)
+
+
+
diff --git a/build/vignette.rds b/build/vignette.rds
new file mode 100644
index 0000000..96b0d9e
Binary files /dev/null and b/build/vignette.rds differ
diff --git a/inst/doc/introduction.R b/inst/doc/introduction.R
new file mode 100644
index 0000000..cf58e71
--- /dev/null
+++ b/inst/doc/introduction.R
@@ -0,0 +1,49 @@
+## ----basic---------------------------------------------------------------
+library(scatterD3)
+scatterD3(x = mtcars$wt, y = mtcars$mpg)
+
+## ----basic_cust----------------------------------------------------------
+scatterD3(x = mtcars$wt, y = mtcars$mpg, point_size = 15, point_opacity = 0.5, fixed = TRUE)
+
+## ----labels--------------------------------------------------------------
+scatterD3(x = mtcars$wt, y = mtcars$mpg, lab = rownames(mtcars), labels_size = 9)
+
+## ----mapping-------------------------------------------------------------
+scatterD3(x = mtcars$wt, y = mtcars$mpg, col_var = mtcars$cyl, symbol_var = mtcars$gear)
+
+## ----map_size------------------------------------------------------------
+scatterD3(x = mtcars$wt, y = mtcars$mpg, col_var = mtcars$cyl, size_var = mtcars$hp, 
+          size_range = c(10,1000), point_opacity = 0.7)
+
+## ----axis_limits---------------------------------------------------------
+scatterD3(x = mtcars$wt, y = mtcars$mpg, xlim=c(0,10), ylim=c(10,35))
+
+## ----cust_labels---------------------------------------------------------
+scatterD3(x = mtcars$wt, y = mtcars$mpg, col_var = mtcars$cyl, symbol_var = mtcars$gear,
+          xlab = "Weight", ylab = "Mpg", col_lab = "Cylinders", symbol_lab = "Gears")
+
+## ----cust_tooltips-------------------------------------------------------
+tooltips <- paste("This is an incredible <strong>", rownames(mtcars),"</strong><br />with ", 
+                  mtcars$cyl, "cylinders !")
+scatterD3(x = mtcars$wt, y = mtcars$mpg, tooltip_text = tooltips)
+
+## ----ellipses------------------------------------------------------------
+scatterD3(x = mtcars$wt, y = mtcars$mpg, ellipses = TRUE)
+
+## ----ellipses_col--------------------------------------------------------
+scatterD3(x = mtcars$wt, y = mtcars$mpg, col_var = mtcars$cyl, ellipses = TRUE)
+
+## ----lasso---------------------------------------------------------------
+scatterD3(x = mtcars$wt, y = mtcars$mpg, lab = rownames(mtcars), lasso = TRUE)
+
+## ----lasso_callback------------------------------------------------------
+scatterD3(x = mtcars$wt, y = mtcars$mpg, lab = rownames(mtcars), 
+          lasso = TRUE,
+          lasso_callback = "function(sel) {alert(sel.data().map(function(d) {return d.lab}).join('\\n'));}")
+
+## ----cust_arrows---------------------------------------------------------
+scatterD3(x = c(1, 0.9, 0.7, 0.2, -0.4, -0.5), xlab = "x",
+          y = c(1, 0.1, -0.5, 0.5, -0.6, 0.7), ylab = "y",
+          lab = LETTERS[1:6], type_var = c("point", rep("arrow", 5)),
+          unit_circle = TRUE, fixed = TRUE, xlim = c(-1.2, 1.2))
+
diff --git a/inst/doc/introduction.Rmd b/inst/doc/introduction.Rmd
new file mode 100644
index 0000000..0ccc163
--- /dev/null
+++ b/inst/doc/introduction.Rmd
@@ -0,0 +1,177 @@
+---
+title: "Interactive scatterplots with scatterD3"
+author: "Julien Barnier"
+date: "`r Sys.Date()`"
+output: 
+  rmarkdown::html_vignette:
+    fig_width: 5
+    toc: true
+vignette: >
+  %\VignetteIndexEntry{Introduction}
+  %\VignetteEngine{knitr::rmarkdown}
+  %\VignetteEncoding{UTF-8}
+---
+
+The `scatterD3` package provides an HTML widget based on the `htmlwidgets` package and allows to produce interactive scatterplots by using the `d3.js` javascript visualization library.
+
+## Basic scatterplot
+
+Starting with the sample `mtcars` dataset, we can produce a basic scatterplot with the following command :
+
+```{r basic}
+library(scatterD3)
+scatterD3(x = mtcars$wt, y = mtcars$mpg)
+```
+
+This will display a simple visualization with the given variables as `x` and `y` axis. There are several interactive features directly available :
+
+- you can zoom in and out with the mouse wheel while the mouse cursor is on the plot
+- you can pan the plot by dragging with your mouse
+- by hovering over a point, you can display a small tooltip window giving the `x` and `y` values
+
+You can customize the points size with the `point_size` parameter, their opacity with `point_opacity`, and you can force the plot to have a 1:1 fixed aspect ratio with `fixed = TRUE`.
+
+```{r basic_cust}
+scatterD3(x = mtcars$wt, y = mtcars$mpg, point_size = 15, point_opacity = 0.5, fixed = TRUE)
+```
+
+## Point labels
+
+You can add text labels to the points by passing a character vector to the `lab` parameter. Labels size are controlled by the `labels_size` parameter.
+
+```{r labels}
+scatterD3(x = mtcars$wt, y = mtcars$mpg, lab = rownames(mtcars), labels_size = 9)
+```
+
+Note that text labels are fully movable : click and drag a label with your mouse to place it where you want. Custom positions are preserved while zooming/panning.
+
+
+## Mapping colors, symbols and size to variables
+
+By passing vectors to the `col_var` and/or `symbol_var` arguments, you can map points colors and symbols to other variables.
+
+```{r mapping}
+scatterD3(x = mtcars$wt, y = mtcars$mpg, col_var = mtcars$cyl, symbol_var = mtcars$gear)
+```
+
+A legend is then automatically added. You can manually specify its width with the `legend_width` argument. Use `legend_width = 0` to disable it entirely.
+
+Note that when hovering over a legend item with your mouse, the corresponding points are highlighted. Also note that the mapped variables values are automatically added to the default tooltips.
+
+You can also map symbol sizes with a variable with the `size_var` argument. `size_range` allows to customize the sizes range :
+
+```{r map_size}
+scatterD3(x = mtcars$wt, y = mtcars$mpg, col_var = mtcars$cyl, size_var = mtcars$hp, 
+          size_range = c(10,1000), point_opacity = 0.7)
+```
+
+## Axis limits
+
+You can manually specify the `x` or `y` axis limits with the `xlim` and `ylim` arguments :
+
+```{r axis_limits}
+scatterD3(x = mtcars$wt, y = mtcars$mpg, xlim=c(0,10), ylim=c(10,35))
+```
+
+
+
+## Custom axis and legend labels
+
+You can customize the axis and legend labels with `xlab`, `ylab`, `col_lab`, `symbol_lab` and `size_lab` :
+
+```{r cust_labels}
+scatterD3(x = mtcars$wt, y = mtcars$mpg, col_var = mtcars$cyl, symbol_var = mtcars$gear,
+          xlab = "Weight", ylab = "Mpg", col_lab = "Cylinders", symbol_lab = "Gears")
+```
+
+Note that default tooltips are updated accordingly.
+
+
+## Custom tooltips
+
+If the default tooltips don't suit your needs, you can customize them by providing a character vector to the `tooltip_text` argument. This can contain HTML tags for formatting.
+
+```{r cust_tooltips}
+tooltips <- paste("This is an incredible <strong>", rownames(mtcars),"</strong><br />with ", 
+                  mtcars$cyl, "cylinders !")
+scatterD3(x = mtcars$wt, y = mtcars$mpg, tooltip_text = tooltips)
+```
+
+You can also disable tooltips entirely with `tooltips = FALSE`.
+
+## Confidence ellipses
+
+You can draw a confidence ellipse around the points :
+
+```{r ellipses}
+scatterD3(x = mtcars$wt, y = mtcars$mpg, ellipses = TRUE)
+```
+
+Or around the different groups of points defined by `col_var` :
+
+```{r ellipses_col}
+scatterD3(x = mtcars$wt, y = mtcars$mpg, col_var = mtcars$cyl, ellipses = TRUE)
+```
+
+Ellipses are computed by the  `ellipse.default()` function of the [ellipse package](https://cran.r-project.org/package=ellipse). The confidence level can be changed with the `ellipse_level` argument (`0.95` by default).
+
+## Lasso selection tool
+
+Thanks to the [d3-lasso-plugin](https://github.com/skokenes/D3-Lasso-Plugin) integration made by @[timelyportfolio](https://github.com/timelyportfolio), you can select and highlight points with a lasso selection tool. To activate it, just add a `lasso = TRUE` argument. The tool is used by shift-clicking and dragging on the plot area (if it doesn't activate, click on the chart first to give it focus).
+
+```{r lasso}
+scatterD3(x = mtcars$wt, y = mtcars$mpg, lab = rownames(mtcars), lasso = TRUE)
+```
+
+To undo the selection, just shift-click again.
+
+You can specify a custom JavaScript callback function to be called by passing it to the `lasso_callback` argument as a character string. This function should accept a `sel` argument, which is a d3 selection of selected points.
+
+Here is an example which shows an alert with selected point labels :
+
+```{r lasso_callback}
+scatterD3(x = mtcars$wt, y = mtcars$mpg, lab = rownames(mtcars), 
+          lasso = TRUE,
+          lasso_callback = "function(sel) {alert(sel.data().map(function(d) {return d.lab}).join('\\n'));}")
+```
+
+
+## Other options
+
+Finally, and for more specific use cases, you can represent some points as an arrow starting from the origin by using the `type_var` argument, and you can add a unit circle with `unit_circle = TRUE`.
+
+```{r cust_arrows}
+scatterD3(x = c(1, 0.9, 0.7, 0.2, -0.4, -0.5), xlab = "x",
+          y = c(1, 0.1, -0.5, 0.5, -0.6, 0.7), ylab = "y",
+          lab = LETTERS[1:6], type_var = c("point", rep("arrow", 5)),
+          unit_circle = TRUE, fixed = TRUE, xlim = c(-1.2, 1.2))
+```
+
+
+## Shiny integration
+
+### Transitions
+
+Like every R HTML widget, shiny integration is straightforward. But as a D3 widget, `scatterD3` is *updatable* : changes in settings or data can be displayed via smooth transitions instead of a complete chart redraw, which can provide interesting visual clues.
+
+For a small demonstration of these transitions, you can take a look at the
+[sample scatterD3 shiny app](http://data.nozav.org/app/scatterD3/).
+
+Enabling transitions in your shiny app is quite simple, you just have to add the `transitions = TRUE` argument to your `scatterD3` calls in your shiny server code. There's only one warning : if your shiny application may filter on your dataset rows via a form control, then you must provide a `key_var` variable that uniquely and persistently identify your rows.
+
+
+### Additional controls : Reset zoom and SVG export
+
+Furthermore, `scatterD3` provides some additional handlers for two interactive features : SVG export and zoom resetting.
+
+By default, you just have to give the following `id` to the corresponding form controls :
+
+- `#scatterD3-reset-zoom` : reset zoom to default on click
+- `#scatterD3-svg-export` : link to download the currently displayed figure as an SVG file
+
+If you are not happy with these ids, you can specify their names yourself with the arguments `dom_id_svg_export` and `dom_id_reset_zoom`.
+
+### Sample app and source code
+
+The
+[sample scatterD3 shiny app](http://data.nozav.org/app/scatterD3/) allows you to see the different features described here. You can [check its source code on GitHub](https://github.com/juba/scatterD3_shiny_app) for a better understanding of the different arguments.
diff --git a/inst/doc/introduction.html b/inst/doc/introduction.html
new file mode 100644
index 0000000..ad452aa
--- /dev/null
+++ b/inst/doc/introduction.html
@@ -0,0 +1,228 @@
+<!DOCTYPE html>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+
+<meta charset="utf-8">
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="generator" content="pandoc" />
+<meta name="viewport" content="width=device-width, initial-scale=1">
+
+<meta name="author" content="Julien Barnier" />
+
+<meta name="date" content="2016-03-24" />
+
+<title>Interactive scatterplots with scatterD3</title>
+
+<script src="data:application/x-javascript,%28function%28%29%20%7B%0A%20%20%2F%2F%20If%20window%2EHTMLWidgets%20is%20already%20defined%2C%20then%20use%20it%3B%20otherwise%20create%20a%0A%20%20%2F%2F%20new%20object%2E%20This%20allows%20preceding%20code%20to%20set%20options%20that%20affect%20the%0A%20%20%2F%2F%20initialization%20process%20%28though%20none%20currently%20exist%29%2E%0A%20%20window%2EHTMLWidgets%20%3D%20window%2EHTMLWidgets%20%7C%7C%20%7B%7D%3B%0A%0A%20%20%2F%2F%20See%20if%20 [...]
+<link href="data:text/css,%2EscatterD3%2Dtooltip%20%7B%0A%20%20%20%20position%3A%20absolute%3B%0A%20%20%20%20color%3A%20%23222%3B%0A%20%20%20%20background%3A%20%23fff%3B%0A%20%20%20%20padding%3A%20%2E5em%3B%0A%20%20%20%20text%2Dshadow%3A%20%23f5f5f5%200%201px%200%3B%0A%20%20%20%20border%2Dradius%3A%202px%3B%0A%20%20%20%20box%2Dshadow%3A%200px%200px%202px%200px%20%23a6a6a6%3B%0A%20%20%20%20opacity%3A%200%2E9%3B%0A%20%20%20%20font%2Dfamily%3A%20sans%2Dserif%3B%0A%20%20%20%20font%2Dsize%3A% [...]
+<script src="data:application/x-javascript,%21function%28%29%7Bfunction%20n%28n%29%7Breturn%20n%26%26%28n%2EownerDocument%7C%7Cn%2Edocument%7C%7Cn%29%2EdocumentElement%7Dfunction%20t%28n%29%7Breturn%20n%26%26%28n%2EownerDocument%26%26n%2EownerDocument%2EdefaultView%7C%7Cn%2Edocument%26%26n%7C%7Cn%2EdefaultView%29%7Dfunction%20e%28n%2Ct%29%7Breturn%20t%3En%3F%2D1%3An%3Et%3F1%3An%3E%3Dt%3F0%3A0%2F0%7Dfunction%20r%28n%29%7Breturn%20null%3D%3D%3Dn%3F0%2F0%3A%2Bn%7Dfunction%20u%28n%29%7Bretur [...]
+<script src="data:application/x-javascript,%21function%20a%28b%2Cc%2Cd%29%7Bfunction%20e%28g%2Ch%29%7Bif%28%21c%5Bg%5D%29%7Bif%28%21b%5Bg%5D%29%7Bvar%20i%3D%22function%22%3D%3Dtypeof%20require%26%26require%3Bif%28%21h%26%26i%29return%20i%28g%2C%210%29%3Bif%28f%29return%20f%28g%2C%210%29%3Bvar%20j%3Dnew%20Error%28%22Cannot%20find%20module%20%27%22%2Bg%2B%22%27%22%29%3Bthrow%20j%2Ecode%3D%22MODULE%5FNOT%5FFOUND%22%2Cj%7Dvar%20k%3Dc%5Bg%5D%3D%7Bexports%3A%7B%7D%7D%3Bb%5Bg%5D%5B0%5D%2Ecall%2 [...]
+<link href="data:text/css,%2Elasso%20path%20%7B%0A%20%20stroke%3A%20rgb%2880%2C80%2C80%29%3B%0A%20%20stroke%2Dwidth%3A%202px%3B%0A%7D%0A%0A%2Elasso%20%2Edrawn%20%7B%0A%20%20fill%3A%20%23CCCCCC%3B%0A%20%20fill%2Dopacity%3A%20%2E15%20%3B%0A%7D%0A%0A%2Elasso%20%2Eloop%5Fclose%20%7B%0A%20%20fill%3A%20none%3B%0A%20%20stroke%2Ddasharray%3A%204%2C4%3B%0A%7D%0A%0A%2Elasso%20%2Eorigin%20%7B%0A%20%20fill%3A%20%233399FF%3B%0A%20%20fill%2Dopacity%3A%20%2E5%3B%0A%7D%0A%0A%2EscatterD3%20%2Enot%2Dpossi [...]
+<script src="data:application/x-javascript,d3%2Elasso%20%3D%20function%28%29%20%7B%0A%0A%20%20%20%20var%20items%20%3D%20null%2C%0A%20%20%20%20%20%20%20%20closePathDistance%20%3D%2075%2C%0A%20%20%20%20%20%20%20%20closePathSelect%20%3D%20true%2C%0A%20%20%20%20%20%20%20%20isPathClosed%20%3D%20false%2C%0A%20%20%20%20%20%20%20%20hoverSelect%20%3D%20true%2C%0A%20%20%20%20%20%20%20%20points%20%3D%20%5B%5D%2C%0A%20%20%20%20%20%20%20%20area%20%3D%20null%2C%0A%20%20%20%20%20%20%20%20on%20%3D%20%7B [...]
+<script src="data:application/x-javascript,function%20scatterD3%28%29%20%7B%0A%0A%20%20%20%20var%20width%20%3D%20600%2C%20%2F%2F%20default%20width%0A%20%20%20%20height%20%3D%20600%2C%20%2F%2F%20default%20height%0A%20%20%20%20dims%20%3D%20%7B%7D%2C%0A%20%20%20%20margin%20%3D%20%7Btop%3A%205%2C%20right%3A%2010%2C%20bottom%3A%2020%2C%20left%3A%2050%2C%20legend%5Ftop%3A%2050%7D%2C%0A%20%20%20%20settings%20%3D%20%7B%7D%2C%0A%20%20%20%20data%20%3D%20%5B%5D%2C%0A%20%20%20%20x%2C%20y%2C%20color% [...]
+
+
+<style type="text/css">code{white-space: pre;}</style>
+<style type="text/css">
+table.sourceCode, tr.sourceCode, td.lineNumbers, td.sourceCode {
+  margin: 0; padding: 0; vertical-align: baseline; border: none; }
+table.sourceCode { width: 100%; line-height: 100%; }
+td.lineNumbers { text-align: right; padding-right: 4px; padding-left: 4px; color: #aaaaaa; border-right: 1px solid #aaaaaa; }
+td.sourceCode { padding-left: 5px; }
+code > span.kw { color: #007020; font-weight: bold; }
+code > span.dt { color: #902000; }
+code > span.dv { color: #40a070; }
+code > span.bn { color: #40a070; }
+code > span.fl { color: #40a070; }
+code > span.ch { color: #4070a0; }
+code > span.st { color: #4070a0; }
+code > span.co { color: #60a0b0; font-style: italic; }
+code > span.ot { color: #007020; }
+code > span.al { color: #ff0000; font-weight: bold; }
+code > span.fu { color: #06287e; }
+code > span.er { color: #ff0000; font-weight: bold; }
+</style>
+
+
+<link href="data:text/css,body%20%7B%0A%20%20background%2Dcolor%3A%20%23fff%3B%0A%20%20margin%3A%201em%20auto%3B%0A%20%20max%2Dwidth%3A%20700px%3B%0A%20%20overflow%3A%20visible%3B%0A%20%20padding%2Dleft%3A%202em%3B%0A%20%20padding%2Dright%3A%202em%3B%0A%20%20font%2Dfamily%3A%20%22Open%20Sans%22%2C%20%22Helvetica%20Neue%22%2C%20Helvetica%2C%20Arial%2C%20sans%2Dserif%3B%0A%20%20font%2Dsize%3A%2014px%3B%0A%20%20line%2Dheight%3A%201%2E35%3B%0A%7D%0A%0A%23header%20%7B%0A%20%20text%2Dalign%3A% [...]
+
+</head>
+
+<body>
+
+
+
+<div class="fluid-row" id="header">
+
+
+<h1 class="title">Interactive scatterplots with scatterD3</h1>
+<h4 class="author"><em>Julien Barnier</em></h4>
+<h4 class="date"><em>2016-03-24</em></h4>
+
+</div>
+
+<div id="TOC">
+<ul>
+<li><a href="#basic-scatterplot">Basic scatterplot</a></li>
+<li><a href="#point-labels">Point labels</a></li>
+<li><a href="#mapping-colors-symbols-and-size-to-variables">Mapping colors, symbols and size to variables</a></li>
+<li><a href="#axis-limits">Axis limits</a></li>
+<li><a href="#custom-axis-and-legend-labels">Custom axis and legend labels</a></li>
+<li><a href="#custom-tooltips">Custom tooltips</a></li>
+<li><a href="#confidence-ellipses">Confidence ellipses</a></li>
+<li><a href="#lasso-selection-tool">Lasso selection tool</a></li>
+<li><a href="#other-options">Other options</a></li>
+<li><a href="#shiny-integration">Shiny integration</a><ul>
+<li><a href="#transitions">Transitions</a></li>
+<li><a href="#additional-controls-reset-zoom-and-svg-export">Additional controls : Reset zoom and SVG export</a></li>
+<li><a href="#sample-app-and-source-code">Sample app and source code</a></li>
+</ul></li>
+</ul>
+</div>
+
+<p>The <code>scatterD3</code> package provides an HTML widget based on the <code>htmlwidgets</code> package and allows to produce interactive scatterplots by using the <code>d3.js</code> javascript visualization library.</p>
+<div id="basic-scatterplot" class="section level2">
+<h2>Basic scatterplot</h2>
+<p>Starting with the sample <code>mtcars</code> dataset, we can produce a basic scatterplot with the following command :</p>
+<pre class="sourceCode r"><code class="sourceCode r"><span class="kw">library</span>(scatterD3)
+<span class="kw">scatterD3</span>(<span class="dt">x =</span> mtcars$wt, <span class="dt">y =</span> mtcars$mpg)</code></pre>
+<p><div id="htmlwidget-8193" style="width:480px;height:288px;" class="scatterD3 html-widget"></div>
+<script type="application/json" data-for="htmlwidget-8193">{"x":{"data":{"x":[2.62,2.875,2.32,3.215,3.44,3.46,3.57,3.19,3.15,3.44,3.44,4.07,3.73,3.78,5.25,5.424,5.345,2.2,1.615,1.835,2.465,3.52,3.435,3.84,3.845,1.935,2.14,1.513,3.17,2.77,3.57,2.78],"y":[21,21,22.8,21.4,18.7,18.1,14.3,24.4,22.8,19.2,17.8,16.4,17.3,15.2,10.4,10.4,14.7,32.4,30.4,33.9,21.5,15.5,15.2,13.3,19.2,27.3,26,30.4,15.8,19.7,15,21.4],"point_opacity":[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],"ke [...]
+<p>This will display a simple visualization with the given variables as <code>x</code> and <code>y</code> axis. There are several interactive features directly available :</p>
+<ul>
+<li>you can zoom in and out with the mouse wheel while the mouse cursor is on the plot</li>
+<li>you can pan the plot by dragging with your mouse</li>
+<li>by hovering over a point, you can display a small tooltip window giving the <code>x</code> and <code>y</code> values</li>
+</ul>
+<p>You can customize the points size with the <code>point_size</code> parameter, their opacity with <code>point_opacity</code>, and you can force the plot to have a 1:1 fixed aspect ratio with <code>fixed = TRUE</code>.</p>
+<pre class="sourceCode r"><code class="sourceCode r"><span class="kw">scatterD3</span>(<span class="dt">x =</span> mtcars$wt, <span class="dt">y =</span> mtcars$mpg, <span class="dt">point_size =</span> <span class="dv">15</span>, <span class="dt">point_opacity =</span> <span class="fl">0.5</span>, <span class="dt">fixed =</span> <span class="ot">TRUE</span>)</code></pre>
+<p><div id="htmlwidget-9853" style="width:480px;height:288px;" class="scatterD3 html-widget"></div>
+<script type="application/json" data-for="htmlwidget-9853">{"x":{"data":{"x":[2.62,2.875,2.32,3.215,3.44,3.46,3.57,3.19,3.15,3.44,3.44,4.07,3.73,3.78,5.25,5.424,5.345,2.2,1.615,1.835,2.465,3.52,3.435,3.84,3.845,1.935,2.14,1.513,3.17,2.77,3.57,2.78],"y":[21,21,22.8,21.4,18.7,18.1,14.3,24.4,22.8,19.2,17.8,16.4,17.3,15.2,10.4,10.4,14.7,32.4,30.4,33.9,21.5,15.5,15.2,13.3,19.2,27.3,26,30.4,15.8,19.7,15,21.4],"point_opacity":[0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5, [...]
+</div>
+<div id="point-labels" class="section level2">
+<h2>Point labels</h2>
+<p>You can add text labels to the points by passing a character vector to the <code>lab</code> parameter. Labels size are controlled by the <code>labels_size</code> parameter.</p>
+<pre class="sourceCode r"><code class="sourceCode r"><span class="kw">scatterD3</span>(<span class="dt">x =</span> mtcars$wt, <span class="dt">y =</span> mtcars$mpg, <span class="dt">lab =</span> <span class="kw">rownames</span>(mtcars), <span class="dt">labels_size =</span> <span class="dv">9</span>)</code></pre>
+<p><div id="htmlwidget-5294" style="width:480px;height:288px;" class="scatterD3 html-widget"></div>
+<script type="application/json" data-for="htmlwidget-5294">{"x":{"data":{"x":[2.62,2.875,2.32,3.215,3.44,3.46,3.57,3.19,3.15,3.44,3.44,4.07,3.73,3.78,5.25,5.424,5.345,2.2,1.615,1.835,2.465,3.52,3.435,3.84,3.845,1.935,2.14,1.513,3.17,2.77,3.57,2.78],"y":[21,21,22.8,21.4,18.7,18.1,14.3,24.4,22.8,19.2,17.8,16.4,17.3,15.2,10.4,10.4,14.7,32.4,30.4,33.9,21.5,15.5,15.2,13.3,19.2,27.3,26,30.4,15.8,19.7,15,21.4],"lab":["Mazda RX4","Mazda RX4 Wag","Datsun 710","Hornet 4 Drive","Hornet Sportabout", [...]
+<p>Note that text labels are fully movable : click and drag a label with your mouse to place it where you want. Custom positions are preserved while zooming/panning.</p>
+</div>
+<div id="mapping-colors-symbols-and-size-to-variables" class="section level2">
+<h2>Mapping colors, symbols and size to variables</h2>
+<p>By passing vectors to the <code>col_var</code> and/or <code>symbol_var</code> arguments, you can map points colors and symbols to other variables.</p>
+<pre class="sourceCode r"><code class="sourceCode r"><span class="kw">scatterD3</span>(<span class="dt">x =</span> mtcars$wt, <span class="dt">y =</span> mtcars$mpg, <span class="dt">col_var =</span> mtcars$cyl, <span class="dt">symbol_var =</span> mtcars$gear)</code></pre>
+<p><div id="htmlwidget-8420" style="width:480px;height:288px;" class="scatterD3 html-widget"></div>
+<script type="application/json" data-for="htmlwidget-8420">{"x":{"data":{"x":[2.62,2.875,2.32,3.215,3.44,3.46,3.57,3.19,3.15,3.44,3.44,4.07,3.73,3.78,5.25,5.424,5.345,2.2,1.615,1.835,2.465,3.52,3.435,3.84,3.845,1.935,2.14,1.513,3.17,2.77,3.57,2.78],"y":[21,21,22.8,21.4,18.7,18.1,14.3,24.4,22.8,19.2,17.8,16.4,17.3,15.2,10.4,10.4,14.7,32.4,30.4,33.9,21.5,15.5,15.2,13.3,19.2,27.3,26,30.4,15.8,19.7,15,21.4],"point_opacity":[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],"co [...]
+<p>A legend is then automatically added. You can manually specify its width with the <code>legend_width</code> argument. Use <code>legend_width = 0</code> to disable it entirely.</p>
+<p>Note that when hovering over a legend item with your mouse, the corresponding points are highlighted. Also note that the mapped variables values are automatically added to the default tooltips.</p>
+<p>You can also map symbol sizes with a variable with the <code>size_var</code> argument. <code>size_range</code> allows to customize the sizes range :</p>
+<pre class="sourceCode r"><code class="sourceCode r"><span class="kw">scatterD3</span>(<span class="dt">x =</span> mtcars$wt, <span class="dt">y =</span> mtcars$mpg, <span class="dt">col_var =</span> mtcars$cyl, <span class="dt">size_var =</span> mtcars$hp, 
+          <span class="dt">size_range =</span> <span class="kw">c</span>(<span class="dv">10</span>,<span class="dv">1000</span>), <span class="dt">point_opacity =</span> <span class="fl">0.7</span>)</code></pre>
+<pre><code>## Warning in scatterD3(x = mtcars$wt, y = mtcars$mpg, col_var = mtcars$cyl, :
+## NA values in size_var. Values set to min(0, size_var)</code></pre>
+<p><div id="htmlwidget-3206" style="width:480px;height:288px;" class="scatterD3 html-widget"></div>
+<script type="application/json" data-for="htmlwidget-3206">{"x":{"data":{"x":[2.62,2.875,2.32,3.215,3.44,3.46,3.57,3.19,3.15,3.44,3.44,4.07,3.73,3.78,5.25,5.424,5.345,2.2,1.615,1.835,2.465,3.52,3.435,3.84,3.845,1.935,2.14,1.513,3.17,2.77,3.57,2.78],"y":[21,21,22.8,21.4,18.7,18.1,14.3,24.4,22.8,19.2,17.8,16.4,17.3,15.2,10.4,10.4,14.7,32.4,30.4,33.9,21.5,15.5,15.2,13.3,19.2,27.3,26,30.4,15.8,19.7,15,21.4],"point_opacity":[0.7,0.7,0.7,0.7,0.7,0.7,0.7,0.7,0.7,0.7,0.7,0.7,0.7,0.7,0.7,0.7,0.7, [...]
+</div>
+<div id="axis-limits" class="section level2">
+<h2>Axis limits</h2>
+<p>You can manually specify the <code>x</code> or <code>y</code> axis limits with the <code>xlim</code> and <code>ylim</code> arguments :</p>
+<pre class="sourceCode r"><code class="sourceCode r"><span class="kw">scatterD3</span>(<span class="dt">x =</span> mtcars$wt, <span class="dt">y =</span> mtcars$mpg, <span class="dt">xlim=</span><span class="kw">c</span>(<span class="dv">0</span>,<span class="dv">10</span>), <span class="dt">ylim=</span><span class="kw">c</span>(<span class="dv">10</span>,<span class="dv">35</span>))</code></pre>
+<p><div id="htmlwidget-1869" style="width:480px;height:288px;" class="scatterD3 html-widget"></div>
+<script type="application/json" data-for="htmlwidget-1869">{"x":{"data":{"x":[2.62,2.875,2.32,3.215,3.44,3.46,3.57,3.19,3.15,3.44,3.44,4.07,3.73,3.78,5.25,5.424,5.345,2.2,1.615,1.835,2.465,3.52,3.435,3.84,3.845,1.935,2.14,1.513,3.17,2.77,3.57,2.78],"y":[21,21,22.8,21.4,18.7,18.1,14.3,24.4,22.8,19.2,17.8,16.4,17.3,15.2,10.4,10.4,14.7,32.4,30.4,33.9,21.5,15.5,15.2,13.3,19.2,27.3,26,30.4,15.8,19.7,15,21.4],"point_opacity":[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],"ke [...]
+</div>
+<div id="custom-axis-and-legend-labels" class="section level2">
+<h2>Custom axis and legend labels</h2>
+<p>You can customize the axis and legend labels with <code>xlab</code>, <code>ylab</code>, <code>col_lab</code>, <code>symbol_lab</code> and <code>size_lab</code> :</p>
+<pre class="sourceCode r"><code class="sourceCode r"><span class="kw">scatterD3</span>(<span class="dt">x =</span> mtcars$wt, <span class="dt">y =</span> mtcars$mpg, <span class="dt">col_var =</span> mtcars$cyl, <span class="dt">symbol_var =</span> mtcars$gear,
+          <span class="dt">xlab =</span> <span class="st">"Weight"</span>, <span class="dt">ylab =</span> <span class="st">"Mpg"</span>, <span class="dt">col_lab =</span> <span class="st">"Cylinders"</span>, <span class="dt">symbol_lab =</span> <span class="st">"Gears"</span>)</code></pre>
+<p><div id="htmlwidget-1610" style="width:480px;height:288px;" class="scatterD3 html-widget"></div>
+<script type="application/json" data-for="htmlwidget-1610">{"x":{"data":{"x":[2.62,2.875,2.32,3.215,3.44,3.46,3.57,3.19,3.15,3.44,3.44,4.07,3.73,3.78,5.25,5.424,5.345,2.2,1.615,1.835,2.465,3.52,3.435,3.84,3.845,1.935,2.14,1.513,3.17,2.77,3.57,2.78],"y":[21,21,22.8,21.4,18.7,18.1,14.3,24.4,22.8,19.2,17.8,16.4,17.3,15.2,10.4,10.4,14.7,32.4,30.4,33.9,21.5,15.5,15.2,13.3,19.2,27.3,26,30.4,15.8,19.7,15,21.4],"point_opacity":[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],"co [...]
+<p>Note that default tooltips are updated accordingly.</p>
+</div>
+<div id="custom-tooltips" class="section level2">
+<h2>Custom tooltips</h2>
+<p>If the default tooltips don’t suit your needs, you can customize them by providing a character vector to the <code>tooltip_text</code> argument. This can contain HTML tags for formatting.</p>
+<pre class="sourceCode r"><code class="sourceCode r">tooltips <-<span class="st"> </span><span class="kw">paste</span>(<span class="st">"This is an incredible <strong>"</span>, <span class="kw">rownames</span>(mtcars),<span class="st">"</strong><br />with "</span>, 
+                  mtcars$cyl, <span class="st">"cylinders !"</span>)
+<span class="kw">scatterD3</span>(<span class="dt">x =</span> mtcars$wt, <span class="dt">y =</span> mtcars$mpg, <span class="dt">tooltip_text =</span> tooltips)</code></pre>
+<p><div id="htmlwidget-1143" style="width:480px;height:288px;" class="scatterD3 html-widget"></div>
+<script type="application/json" data-for="htmlwidget-1143">{"x":{"data":{"x":[2.62,2.875,2.32,3.215,3.44,3.46,3.57,3.19,3.15,3.44,3.44,4.07,3.73,3.78,5.25,5.424,5.345,2.2,1.615,1.835,2.465,3.52,3.435,3.84,3.845,1.935,2.14,1.513,3.17,2.77,3.57,2.78],"y":[21,21,22.8,21.4,18.7,18.1,14.3,24.4,22.8,19.2,17.8,16.4,17.3,15.2,10.4,10.4,14.7,32.4,30.4,33.9,21.5,15.5,15.2,13.3,19.2,27.3,26,30.4,15.8,19.7,15,21.4],"point_opacity":[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],"ke [...]
+<p>You can also disable tooltips entirely with <code>tooltips = FALSE</code>.</p>
+</div>
+<div id="confidence-ellipses" class="section level2">
+<h2>Confidence ellipses</h2>
+<p>You can draw a confidence ellipse around the points :</p>
+<pre class="sourceCode r"><code class="sourceCode r"><span class="kw">scatterD3</span>(<span class="dt">x =</span> mtcars$wt, <span class="dt">y =</span> mtcars$mpg, <span class="dt">ellipses =</span> <span class="ot">TRUE</span>)</code></pre>
+<p><div id="htmlwidget-4279" style="width:480px;height:288px;" class="scatterD3 html-widget"></div>
+<script type="application/json" data-for="htmlwidget-4279">{"x":{"data":{"x":[2.62,2.875,2.32,3.215,3.44,3.46,3.57,3.19,3.15,3.44,3.44,4.07,3.73,3.78,5.25,5.424,5.345,2.2,1.615,1.835,2.465,3.52,3.435,3.84,3.845,1.935,2.14,1.513,3.17,2.77,3.57,2.78],"y":[21,21,22.8,21.4,18.7,18.1,14.3,24.4,22.8,19.2,17.8,16.4,17.3,15.2,10.4,10.4,14.7,32.4,30.4,33.9,21.5,15.5,15.2,13.3,19.2,27.3,26,30.4,15.8,19.7,15,21.4],"point_opacity":[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],"ke [...]
+<p>Or around the different groups of points defined by <code>col_var</code> :</p>
+<pre class="sourceCode r"><code class="sourceCode r"><span class="kw">scatterD3</span>(<span class="dt">x =</span> mtcars$wt, <span class="dt">y =</span> mtcars$mpg, <span class="dt">col_var =</span> mtcars$cyl, <span class="dt">ellipses =</span> <span class="ot">TRUE</span>)</code></pre>
+<p><div id="htmlwidget-2030" style="width:480px;height:288px;" class="scatterD3 html-widget"></div>
+<script type="application/json" data-for="htmlwidget-2030">{"x":{"data":{"x":[2.62,2.875,2.32,3.215,3.44,3.46,3.57,3.19,3.15,3.44,3.44,4.07,3.73,3.78,5.25,5.424,5.345,2.2,1.615,1.835,2.465,3.52,3.435,3.84,3.845,1.935,2.14,1.513,3.17,2.77,3.57,2.78],"y":[21,21,22.8,21.4,18.7,18.1,14.3,24.4,22.8,19.2,17.8,16.4,17.3,15.2,10.4,10.4,14.7,32.4,30.4,33.9,21.5,15.5,15.2,13.3,19.2,27.3,26,30.4,15.8,19.7,15,21.4],"point_opacity":[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],"co [...]
+<p>Ellipses are computed by the <code>ellipse.default()</code> function of the <a href="https://cran.r-project.org/package=ellipse">ellipse package</a>. The confidence level can be changed with the <code>ellipse_level</code> argument (<code>0.95</code> by default).</p>
+</div>
+<div id="lasso-selection-tool" class="section level2">
+<h2>Lasso selection tool</h2>
+<p>Thanks to the <a href="https://github.com/skokenes/D3-Lasso-Plugin">d3-lasso-plugin</a> integration made by @<a href="https://github.com/timelyportfolio">timelyportfolio</a>, you can select and highlight points with a lasso selection tool. To activate it, just add a <code>lasso = TRUE</code> argument. The tool is used by shift-clicking and dragging on the plot area (if it doesn’t activate, click on the chart first to give it focus).</p>
+<pre class="sourceCode r"><code class="sourceCode r"><span class="kw">scatterD3</span>(<span class="dt">x =</span> mtcars$wt, <span class="dt">y =</span> mtcars$mpg, <span class="dt">lab =</span> <span class="kw">rownames</span>(mtcars), <span class="dt">lasso =</span> <span class="ot">TRUE</span>)</code></pre>
+<p><div id="htmlwidget-2464" style="width:480px;height:288px;" class="scatterD3 html-widget"></div>
+<script type="application/json" data-for="htmlwidget-2464">{"x":{"data":{"x":[2.62,2.875,2.32,3.215,3.44,3.46,3.57,3.19,3.15,3.44,3.44,4.07,3.73,3.78,5.25,5.424,5.345,2.2,1.615,1.835,2.465,3.52,3.435,3.84,3.845,1.935,2.14,1.513,3.17,2.77,3.57,2.78],"y":[21,21,22.8,21.4,18.7,18.1,14.3,24.4,22.8,19.2,17.8,16.4,17.3,15.2,10.4,10.4,14.7,32.4,30.4,33.9,21.5,15.5,15.2,13.3,19.2,27.3,26,30.4,15.8,19.7,15,21.4],"lab":["Mazda RX4","Mazda RX4 Wag","Datsun 710","Hornet 4 Drive","Hornet Sportabout", [...]
+<p>To undo the selection, just shift-click again.</p>
+<p>You can specify a custom JavaScript callback function to be called by passing it to the <code>lasso_callback</code> argument as a character string. This function should accept a <code>sel</code> argument, which is a d3 selection of selected points.</p>
+<p>Here is an example which shows an alert with selected point labels :</p>
+<pre class="sourceCode r"><code class="sourceCode r"><span class="kw">scatterD3</span>(<span class="dt">x =</span> mtcars$wt, <span class="dt">y =</span> mtcars$mpg, <span class="dt">lab =</span> <span class="kw">rownames</span>(mtcars), 
+          <span class="dt">lasso =</span> <span class="ot">TRUE</span>,
+          <span class="dt">lasso_callback =</span> <span class="st">"function(sel) {alert(sel.data().map(function(d) {return d.lab}).join('</span><span class="ch">\\</span><span class="st">n'));}"</span>)</code></pre>
+<p><div id="htmlwidget-7477" style="width:480px;height:288px;" class="scatterD3 html-widget"></div>
+<script type="application/json" data-for="htmlwidget-7477">{"x":{"data":{"x":[2.62,2.875,2.32,3.215,3.44,3.46,3.57,3.19,3.15,3.44,3.44,4.07,3.73,3.78,5.25,5.424,5.345,2.2,1.615,1.835,2.465,3.52,3.435,3.84,3.845,1.935,2.14,1.513,3.17,2.77,3.57,2.78],"y":[21,21,22.8,21.4,18.7,18.1,14.3,24.4,22.8,19.2,17.8,16.4,17.3,15.2,10.4,10.4,14.7,32.4,30.4,33.9,21.5,15.5,15.2,13.3,19.2,27.3,26,30.4,15.8,19.7,15,21.4],"lab":["Mazda RX4","Mazda RX4 Wag","Datsun 710","Hornet 4 Drive","Hornet Sportabout", [...]
+</div>
+<div id="other-options" class="section level2">
+<h2>Other options</h2>
+<p>Finally, and for more specific use cases, you can represent some points as an arrow starting from the origin by using the <code>type_var</code> argument, and you can add a unit circle with <code>unit_circle = TRUE</code>.</p>
+<pre class="sourceCode r"><code class="sourceCode r"><span class="kw">scatterD3</span>(<span class="dt">x =</span> <span class="kw">c</span>(<span class="dv">1</span>, <span class="fl">0.9</span>, <span class="fl">0.7</span>, <span class="fl">0.2</span>, -<span class="fl">0.4</span>, -<span class="fl">0.5</span>), <span class="dt">xlab =</span> <span class="st">"x"</span>,
+          <span class="dt">y =</span> <span class="kw">c</span>(<span class="dv">1</span>, <span class="fl">0.1</span>, -<span class="fl">0.5</span>, <span class="fl">0.5</span>, -<span class="fl">0.6</span>, <span class="fl">0.7</span>), <span class="dt">ylab =</span> <span class="st">"y"</span>,
+          <span class="dt">lab =</span> LETTERS[<span class="dv">1</span>:<span class="dv">6</span>], <span class="dt">type_var =</span> <span class="kw">c</span>(<span class="st">"point"</span>, <span class="kw">rep</span>(<span class="st">"arrow"</span>, <span class="dv">5</span>)),
+          <span class="dt">unit_circle =</span> <span class="ot">TRUE</span>, <span class="dt">fixed =</span> <span class="ot">TRUE</span>, <span class="dt">xlim =</span> <span class="kw">c</span>(-<span class="fl">1.2</span>, <span class="fl">1.2</span>))</code></pre>
+<p><div id="htmlwidget-4340" style="width:480px;height:288px;" class="scatterD3 html-widget"></div>
+<script type="application/json" data-for="htmlwidget-4340">{"x":{"data":{"x":[1,0.9,0.7,0.2,-0.4,-0.5],"y":[1,0.1,-0.5,0.5,-0.6,0.7],"lab":["A","B","C","D","E","F"],"point_opacity":[1,1,1,1,1,1],"type_var":["point","arrow","arrow","arrow","arrow","arrow"],"key_var":[1,2,3,4,5,6]},"settings":{"labels_size":10,"point_size":64,"xlab":"x","ylab":"y","has_labels":true,"col_var":null,"col_lab":"NULL","colors":null,"ellipses":false,"ellipses_data":[],"symbol_var":null,"symbol_lab":"NULL","size_ [...]
+</div>
+<div id="shiny-integration" class="section level2">
+<h2>Shiny integration</h2>
+<div id="transitions" class="section level3">
+<h3>Transitions</h3>
+<p>Like every R HTML widget, shiny integration is straightforward. But as a D3 widget, <code>scatterD3</code> is <em>updatable</em> : changes in settings or data can be displayed via smooth transitions instead of a complete chart redraw, which can provide interesting visual clues.</p>
+<p>For a small demonstration of these transitions, you can take a look at the <a href="http://data.nozav.org/app/scatterD3/">sample scatterD3 shiny app</a>.</p>
+<p>Enabling transitions in your shiny app is quite simple, you just have to add the <code>transitions = TRUE</code> argument to your <code>scatterD3</code> calls in your shiny server code. There’s only one warning : if your shiny application may filter on your dataset rows via a form control, then you must provide a <code>key_var</code> variable that uniquely and persistently identify your rows.</p>
+</div>
+<div id="additional-controls-reset-zoom-and-svg-export" class="section level3">
+<h3>Additional controls : Reset zoom and SVG export</h3>
+<p>Furthermore, <code>scatterD3</code> provides some additional handlers for two interactive features : SVG export and zoom resetting.</p>
+<p>By default, you just have to give the following <code>id</code> to the corresponding form controls :</p>
+<ul>
+<li><code>#scatterD3-reset-zoom</code> : reset zoom to default on click</li>
+<li><code>#scatterD3-svg-export</code> : link to download the currently displayed figure as an SVG file</li>
+</ul>
+<p>If you are not happy with these ids, you can specify their names yourself with the arguments <code>dom_id_svg_export</code> and <code>dom_id_reset_zoom</code>.</p>
+</div>
+<div id="sample-app-and-source-code" class="section level3">
+<h3>Sample app and source code</h3>
+<p>The <a href="http://data.nozav.org/app/scatterD3/">sample scatterD3 shiny app</a> allows you to see the different features described here. You can <a href="https://github.com/juba/scatterD3_shiny_app">check its source code on GitHub</a> for a better understanding of the different arguments.</p>
+</div>
+</div>
+
+
+
+<!-- dynamically load mathjax for compatibility with self-contained -->
+<script>
+  (function () {
+    var script = document.createElement("script");
+    script.type = "text/javascript";
+    script.src  = "https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML";
+    document.getElementsByTagName("head")[0].appendChild(script);
+  })();
+</script>
+
+</body>
+</html>
diff --git a/inst/htmlwidgets/lib/d3-lasso-plugin/LICENSE b/inst/htmlwidgets/lib/d3-lasso-plugin/LICENSE
new file mode 100644
index 0000000..80e25df
--- /dev/null
+++ b/inst/htmlwidgets/lib/d3-lasso-plugin/LICENSE
@@ -0,0 +1,28 @@
+
+Copyright (c) 2015-2016, Speros Kokenes
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors
+  may be used to endorse or promote products derived from this software
+  without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/inst/htmlwidgets/lib/d3-lasso-plugin/README.md b/inst/htmlwidgets/lib/d3-lasso-plugin/README.md
new file mode 100644
index 0000000..42e891f
--- /dev/null
+++ b/inst/htmlwidgets/lib/d3-lasso-plugin/README.md
@@ -0,0 +1,88 @@
+lasso
+=========
+
+lasso.js is a D3 plugin that allows you to tag elements on a page by drawing a line over or around objects. Functions can be run based on the lasso action. This functionality can be useful for brushing or filtering.
+
+An example of the lasso implemented in a scatterplot can be found here: [http://bl.ocks.org/skokenes/511c5b658c405ad68941](http://bl.ocks.org/skokenes/511c5b658c405ad68941)
+
+This example is based off of Mike Bostock's scatterplot example here: [http://bl.ocks.org/mbostock/3887118](http://bl.ocks.org/mbostock/3887118)
+
+Lassoing tags
+--
+When the lasso is used, it tags elements by adding properties to their data. The properties are:
+
+- possible: while drawing a lasso, if an element is part of the final selection that would be made if the lasso was completed at that instance, this value is true. Otherwise, it is false.
+- selected: when a lasso is completed, all elements that were tagged as possible are given a selected value of true. Otherwise, the value is false.
+
+The tags can be used in combination with functions to perform actions like styling the possible or selected values while the lasso is in use.
+
+Note that the lasso only works with elements whose data is defined as an object.
+
+
+Function Overview
+--
+**d3.lasso**()
+
+Creates a new lasso object. This object can then have parameters set before the lasso is drawn.
+```
+var lasso = d3.lasso(); // creates a new lasso
+```
+
+lasso.**items**(_[selection]_)
+
+The items() parameter takes in a d3 selection. Each element in the selection will be tagged with lasso-specific properties when the lasso is used. If no input is specified, the function returns the lasso's current items.
+```
+lasso.items(d3.selectAll("circle")); // sets all circles on the page to be lasso-able
+```
+
+lasso.**hoverSelect**(_[bool]_)
+
+The hoverSelect() parameter takes in a boolean that determines whether objects can be lassoed by hovering over an element during lassoing. The default value is set to true. If no input is specified, the function returns the lasso's current hover parameter.
+```
+lasso.hoverSelect(true); // allows hovering of elements for selection during lassoing
+```
+
+lasso.**closePathSelect**(_[bool]_)
+
+The closePathSelect() parameter takes in a boolean that determines whether objects can be lassoed by drawing a loop around them. The default value is set to true. If no input is specified, the function returns the lasso's current parameter.
+```
+lasso.closePathSelect(true); // allows looping of elements for selection during lassoing
+```
+
+lasso.**closePathDistance**(_[num]_)
+
+The closePathDistance() parameter takes in a number that specifies the maximum distance in pixels from the lasso origin that a lasso needs to be drawn in order to complete the loop and select elements. This parameter only works if closePathSelect is set to true; If no input is specified, the function returns the lasso's current parameter.
+```
+lasso.closePathDistance(75); // the lasso loop will complete itself whenever the lasso end is within 75 pixels of the origin
+```
+
+lasso.**area**(_[sel]_)
+
+The area() parameter takes in a selection representing the element to be used as a target area for the lasso event. If no input is specified, the function returns the current area selection.
+```
+lasso.area(d3.select("#myLassoRect")); // the lasso will be trigger whenever a user clicks and drags on #myLassoRect
+```
+
+lasso.**on**(_type,[func]_)
+
+The on() parameter takes in a type of event and a function for that event. There are 3 types of events that can be defined:
+- start: this function will be executed whenever a lasso is started
+- draw: this function will execute repeatedly as the lasso is drawn
+- end: this function will be executed whenever a lasso is completed
+
+If no function is specified, the function will return the current function defined for the type specified.
+```
+lasso.on("start",function() { alert("lasso started!"); }); // every time a lasso is started, an alert will trigger
+```
+
+Initiating a lasso
+--
+Once a lasso object is defined, it can be added to a page by calling it on an element like an svg.
+```
+var lasso = d3.lasso()
+                .items(d3.selectAll("circle")) // Create a lasso and provide it some target elements
+                .area(de.select("#myLassoRect")); // Sets the drag area for the lasso on the rectangle #myLassoRect
+d3.select("svg").call(lasso); // Initiate the lasso on an svg element
+```
+
+If a lasso is going to be used on graphical elements that have been translated via a g element acting as a container, which is a common practice for incorporating chart margins, then the lasso should be called on that g element so that it is in the same coordinate system as the graphical elements.
\ No newline at end of file
diff --git a/inst/htmlwidgets/lib/d3-lasso-plugin/lasso.css b/inst/htmlwidgets/lib/d3-lasso-plugin/lasso.css
new file mode 100644
index 0000000..898505f
--- /dev/null
+++ b/inst/htmlwidgets/lib/d3-lasso-plugin/lasso.css
@@ -0,0 +1,45 @@
+.lasso path {
+  stroke: rgb(80,80,80);
+  stroke-width: 2px;
+}
+
+.lasso .drawn {
+  fill: #CCCCCC;
+  fill-opacity: .15 ;
+}
+
+.lasso .loop_close {
+  fill: none;
+  stroke-dasharray: 4,4;
+}
+
+.lasso .origin {
+  fill: #3399FF;
+  fill-opacity: .5;
+}
+
+.scatterD3 .not-possible-lasso {
+  fill: rgb(150,150,150);
+  opacity: 1;
+}
+
+.scatterD3 .arrow.not-possible-lasso {
+  stroke: rgb(200,200,200);
+}
+
+.scatterD3 .possible-lasso {
+  fill: #EC888C;
+  opacity: 1;
+}
+
+.scatterD3 .arrow.possible-lasso {
+  stroke: #EC888C;
+}
+
+/*.scatterD3 .selected-lasso {
+  opacity: 1 !important;
+}
+
+.scatterD3 .not-selected-lasso {
+  opacity: 0.1 !important;
+}*/
diff --git a/inst/htmlwidgets/lib/d3-lasso-plugin/lasso.js b/inst/htmlwidgets/lib/d3-lasso-plugin/lasso.js
new file mode 100644
index 0000000..3c65c32
--- /dev/null
+++ b/inst/htmlwidgets/lib/d3-lasso-plugin/lasso.js
@@ -0,0 +1,370 @@
+d3.lasso = function() {
+
+    var items = null,
+        closePathDistance = 75,
+        closePathSelect = true,
+        isPathClosed = false,
+        hoverSelect = true,
+        points = [],
+        area = null,
+        on = {start:function(){}, draw: function(){}, end: function(){}};
+
+    function lasso() {
+        // the element where the lasso was called
+        var _this = d3.select(this[0][0]);
+
+        // add a new group for the lasso
+        var g = _this.append("g")
+                    .attr("class","lasso");
+
+        // add the drawn path for the lasso
+        var dyn_path = g.append("path")
+            .attr("class","drawn");
+
+        // add a path used for calculations
+        var calc_path = g.append("path")
+            .attr("display","none");
+
+        // add a closed path
+        var close_path = g.append("path")
+            .attr("class","loop_close");
+
+        // add a close path used for calculations
+        var calc_close_path = g.append("path")
+            .attr("display","none");
+
+        // add an origin node
+        var origin_node = g.append("circle")
+            .attr("class","origin");
+
+        // The lasso path for calculations
+        var path;
+
+        // The transformed lasso path for rendering
+        var tpath;
+
+        // The lasso origin for calculations
+        var origin;
+
+        // The transformed lasso origin for rendering
+        var torigin;
+
+        // The last known point on the lasso during drag - needed for evaluating edges
+        var last_known_point;
+
+        // The starting point for evaluating the path
+        var path_length_start;
+
+        // Apply drag behaviors
+        var drag = d3.behavior.drag()
+            .on("dragstart", dragstart)
+            .on("drag", dragmove)
+            .on("dragend", dragend);
+
+        // Call drag
+        area.call(drag);
+
+        function dragstart() {
+            // Initialize paths
+            path="";
+            tpath = "";
+            dyn_path.attr("d", "M0 0");
+            close_path.attr("d", "M0 0");
+
+            // Set path length start
+            path_length_start = 0;
+            // Set every item to have a false selection and reset their center point and counters
+            items[0].forEach(function(d) {
+                d.hoverSelected = false;
+                d.loopSelected = false;
+                var box = d.getBoundingClientRect();
+                d.lassoPoint = {
+                    cx: Math.round(box.left + box.width/2),
+                    cy: Math.round(box.top + box.height/2),
+                    edges: {top:0,right:0,bottom:0,left:0},
+                    close_edges: {left: 0, right: 0}
+                };
+
+
+            });
+
+            // if hover is on, add hover function
+            if(hoverSelect===true) {
+                items.on("mouseover.lasso",function() {
+                    // if hovered, change lasso selection attribute to true
+                    d3.select(this)[0][0].hoverSelected = true;
+                });
+            }
+
+            // Run user defined start function
+            on.start();
+        }
+
+        function dragmove() {
+            // Get mouse position within body, used for calculations
+            var x = d3.event.sourceEvent.clientX;
+            var y = d3.event.sourceEvent.clientY;
+            // Get mouse position within drawing area, used for rendering
+            var tx = d3.mouse(this)[0];
+            var ty = d3.mouse(this)[1];
+
+            // Initialize the path or add the latest point to it
+            if (path==="") {
+                path = path + "M " + x + " " + y;
+                tpath = tpath + "M " + tx + " " + ty;
+                origin = [x,y];
+                torigin = [tx,ty];
+                // Draw origin node
+                origin_node
+                    .attr("cx",tx)
+                    .attr("cy",ty)
+                    .attr("r",7)
+                    .attr("display",null);
+            }
+            else {
+                path = path + " L " + x + " " + y;
+                tpath = tpath + " L " + tx + " " + ty;
+            }
+
+            // Reset closed edges counter
+            items[0].forEach(function(d) {
+                d.lassoPoint.close_edges = {left:0,right:0};
+            });
+
+            // Calculate the current distance from the lasso origin
+            var distance = Math.sqrt(Math.pow(x-origin[0],2)+Math.pow(y-origin[1],2));
+
+            // Set the closed path line
+            var close_draw_path = "M " + tx + " " + ty + " L " + torigin[0] + " " + torigin[1];
+
+            // Set the calc closed path line
+            var calc_close_draw_path = "M " + x + " " + y + " L " + origin[0] + " " + origin[1];
+
+            // Draw the lines
+            dyn_path.attr("d",tpath);
+
+            // path for calcs
+            calc_path.attr("d",path);
+
+            calc_close_path.attr("d",calc_close_draw_path);
+
+            // Check if the path is closed
+            isPathClosed = distance<=closePathDistance ? true : false;
+
+            // If within the closed path distance parameter, show the closed path. otherwise, hide it
+            if(isPathClosed) {
+                close_path.attr("display",null);
+            }
+            else {
+                close_path.attr("display","none");
+            }
+
+
+            // Get path length
+            var path_node = calc_path.node();
+            var path_length_end = path_node.getTotalLength();
+            // Get the ending point of the path
+            var last_pos = path_node.getPointAtLength(path_length_start-1);
+
+            // Iterate through each point on the path
+            for (var i = path_length_start; i<=path_length_end; i++) {
+                // Get the current coordinates on the path
+                var cur_pos = path_node.getPointAtLength(i);
+                var cur_pos_obj = {
+                    x:Math.round(cur_pos.x*100)/100,
+                    y:Math.round(cur_pos.y*100)/100,
+                };
+                // Get the prior coordinates on the path
+                var prior_pos = path_node.getPointAtLength(i-1);
+                var prior_pos_obj = {
+                    x:Math.round(prior_pos.x*100)/100,
+                    y:Math.round(prior_pos.y*100)/100,
+                };
+
+                // Iterate through each item
+                items[0].filter(function(d) {
+                    var a;
+                    // If we are on the same y position as the item and we weren't on this y before,
+                    // mark as the last known point. Return false - we don't need to count an edge yet
+                    if(d.lassoPoint.cy === cur_pos_obj.y && d.lassoPoint.cy != prior_pos_obj.y) {
+                        last_known_point = {
+                            x: prior_pos_obj.x,
+                            y: prior_pos_obj.y
+                        };
+                        a=false;
+                    }
+                    // If we are on the same y position as the item and we were on this y before,
+                    // return false - we don't need to count an edge yet
+                    else if (d.lassoPoint.cy === cur_pos_obj.y && d.lassoPoint.cy === prior_pos_obj.y) {
+                        a = false;
+                    }
+                    // If we are not on the same y position as the item but we were previously,
+                    // determine if we passed by the item or came up to it and turned around.
+                    // Return true if we passed it so that we can evaluate for an edge
+                    else if (d.lassoPoint.cy != cur_pos_obj.y && d.lassoPoint.cy === prior_pos_obj.y) {
+                        a = sign(d.lassoPoint.cy-cur_pos_obj.y)!=sign(d.lassoPoint.cy-last_known_point.y);
+                    }
+                    // Else, mark a last known point and check for a crossing.
+                    // If we crossed, we need to evaluate for edges
+                    else {
+                        last_known_point = {
+                            x: prior_pos_obj.x,
+                            y: prior_pos_obj.y
+                        };
+                        a = sign(d.lassoPoint.cy-cur_pos_obj.y)!=sign(d.lassoPoint.cy-prior_pos_obj.y);
+                    }
+                    return a;
+                }).forEach(function(d) {
+                    // Iterate through each object and add an edge to the left or right
+                    if(cur_pos_obj.x>d.lassoPoint.cx) {
+                        d.lassoPoint.edges.right = d.lassoPoint.edges.right+1;
+                    }
+                    if(cur_pos_obj.x<d.lassoPoint.cx) {
+                        d.lassoPoint.edges.left = d.lassoPoint.edges.left+1;
+                    }
+                });
+            }
+
+            // If the path is closed and close select is set to true, draw the closed paths and count edges
+             if(isPathClosed === true && closePathSelect === true) {
+                close_path.attr("d",close_draw_path);
+                close_path_node =calc_close_path.node();
+                var close_path_length = close_path_node.getTotalLength();
+                var close_path_edges = {left:0,right:0};
+                for (var i = 0; i<=close_path_length; i++) {
+                    var cur_pos = close_path_node.getPointAtLength(i);
+                    var prior_pos = close_path_node.getPointAtLength(i-1);
+
+                    items[0].filter(function(d) {return d.lassoPoint.cy==Math.round(cur_pos.y);}).forEach(function(d) {
+                        if(Math.round(cur_pos.y)!=Math.round(prior_pos.y) && Math.round(cur_pos.x)>d.lassoPoint.cx) {
+                            d.lassoPoint.close_edges.right = 1;
+                        }
+                        if(Math.round(cur_pos.y)!=Math.round(prior_pos.y) && Math.round(cur_pos.x)<d.lassoPoint.cx) {
+                            d.lassoPoint.close_edges.left = 1;
+                        }
+                    });
+
+                }
+
+                // Check and see if the points have at least one edge to the left, and an odd # of edges to the right. If so, mark as selected.
+                items[0].forEach(function(a) {
+                    if((a.lassoPoint.edges.left+a.lassoPoint.close_edges.left)>0 && (a.lassoPoint.edges.right + a.lassoPoint.close_edges.right)%2 ==1) {
+                        a.loopSelected = true;
+                    }
+                    else {
+                        a.loopSelected = false;
+                    }
+                });
+            }
+            else {
+                items[0].forEach(function(d) {
+                    d.loopSelected = false;
+                });
+            }
+
+            // Tag possible items
+            d3.selectAll(items[0].filter(function(d) {return (d.loopSelected && isPathClosed) || d.hoverSelected;}))
+                .each(function(d) { d.possible = true;});
+
+            d3.selectAll(items[0].filter(function(d) {return !((d.loopSelected && isPathClosed) || d.hoverSelected);}))
+                .each(function(d) {d.possible = false;});
+
+            on.draw();
+
+            // Continue drawing path from where it left off
+            path_length_start = path_length_end+1;
+        }
+
+        function dragend() {
+            // Remove mouseover tagging function
+            items.on("mouseover.lasso",null);
+
+            // Tag selected items
+            items.filter(function(d) {return d.possible === true;})
+                .each(function(d) {d.selected = true;});
+
+            items.filter(function(d) {return d.possible === false;})
+                .each(function(d) {d.selected = false;});
+
+            // Reset possible items
+            items
+                .each(function(d) {d.possible = false;});
+
+            // Clear lasso
+            dyn_path.attr("d", "M0 0");
+            close_path.attr("d", "M0 0");
+            origin_node.attr("display","none");
+            // Run user defined end function
+            on.end();
+
+        }
+    }
+
+    lasso.items  = function(_) {
+
+        if (!arguments.length) return items;
+        items = _;
+        items[0].forEach(function(d) {
+            var item = d3.select(d);
+            if(typeof item.datum() === 'undefined') {
+                item.datum({possible:false,selected:false});
+            }
+            else {
+                //item.attr("d",function(e) {e.possible = false; e.selected = false; return e;});
+                var e = item.datum();
+                e.possible = false;
+                e.selected = false;
+                item.datum(e);
+            }
+        });
+        return lasso;
+    };
+
+    lasso.closePathDistance  = function(_) {
+        if (!arguments.length) return closePathDistance;
+        closePathDistance = _;
+        return lasso;
+    };
+
+    lasso.closePathSelect = function(_) {
+        if (!arguments.length) return closePathSelect;
+        closePathSelect = _===true ? true : false;
+        return lasso;
+    };
+
+    lasso.isPathClosed = function(_) {
+        if (!arguments.length) return isPathClosed;
+        isPathClosed = _===true ? true : false;
+        return lasso;
+    };
+
+    lasso.hoverSelect = function(_) {
+        if (!arguments.length) return hoverSelect;
+        hoverSelect = _===true ? true : false;
+        return lasso;
+    };
+
+    lasso.on = function(type,_) {
+        if(!arguments.length) return on;
+        if(arguments.length===1) return on[type];
+        var types = ["start","draw","end"];
+        if(types.indexOf(type)>-1) {
+            on[type] = _;
+        }
+        return lasso;
+    };
+
+    lasso.area = function(_) {
+        if(!arguments.length) return area;
+        area=_;
+        return lasso;
+    };
+
+    function sign(x) {
+        return x?x<0?-1:1:0;
+    }
+
+
+    return lasso;
+
+};
diff --git a/inst/htmlwidgets/lib/scatterD3.css b/inst/htmlwidgets/lib/scatterD3.css
new file mode 100644
index 0000000..fa15819
--- /dev/null
+++ b/inst/htmlwidgets/lib/scatterD3.css
@@ -0,0 +1,29 @@
+.scatterD3-tooltip {
+    position: absolute;
+    color: #222;
+    background: #fff;
+    padding: .5em;
+    text-shadow: #f5f5f5 0 1px 0;
+    border-radius: 2px;
+    box-shadow: 0px 0px 2px 0px #a6a6a6;
+    opacity: 0.9;
+    font-family: sans-serif;
+    font-size: 9px;
+    z-index: 10;
+}
+
+.hidden {
+    display: none;
+}
+
+.scatterD3 .point-label {
+  cursor: pointer;
+}
+
+.scatterD3 .pane {
+  cursor: move;
+}
+
+.scatterD3, .scatterD3:focus {
+  outline: 0px solid transparent
+}
diff --git a/inst/htmlwidgets/scatterD3.js b/inst/htmlwidgets/scatterD3.js
new file mode 100644
index 0000000..c737728
--- /dev/null
+++ b/inst/htmlwidgets/scatterD3.js
@@ -0,0 +1,1238 @@
+function scatterD3() {
+
+    var width = 600, // default width
+    height = 600, // default height
+    dims = {},
+    margin = {top: 5, right: 10, bottom: 20, left: 50, legend_top: 50},
+    settings = {},
+    data = [],
+    x, y, color_scale, symbol_scale, size_scale,
+    min_x, min_y, max_x, max_y, gap_x, gap_y,
+    xAxis, yAxis,
+    svg,
+    zeroline, zoom, drag,
+    lasso_base, lasso_classes;
+
+    function setup_sizes() {
+
+        dims.legend_width = 0;
+        if (settings.has_legend) dims.legend_width = settings.legend_width;
+
+        dims.width = width - dims.legend_width;
+        dims.height = height;
+        dims.height = dims.height - margin.top - margin.bottom;
+        dims.width = dims.width - margin.left - margin.right;
+
+        // Fixed ratio
+        if (settings.fixed) {
+            dims.height = Math.min(dims.height, dims.width);
+            dims.width = dims.height;
+        }
+
+        dims.total_width = dims.width + margin.left + margin.right + dims.legend_width;
+        dims.total_height = dims.height + margin.top + margin.bottom;
+
+        dims.legend_x = dims.total_width - margin.right - dims.legend_width + 24;
+    }
+
+    function setup_scales() {
+
+        // x and y limits
+        if (settings.xlim === null) {
+            min_x = d3.min(data, function(d) { return(d.x);} );
+            max_x = d3.max(data, function(d) { return(d.x);} );
+            gap_x = (max_x - min_x) * 0.2;
+        } else {
+            min_x = settings.xlim[0];
+            max_x = settings.xlim[1];
+            gap_x = 0;
+        }
+        if (settings.ylim === null) {
+            min_y = d3.min(data, function(d) { return(d.y);} );
+            max_y = d3.max(data, function(d) { return(d.y);} );
+            gap_y = (max_y - min_y) * 0.2;
+        } else {
+            min_y = settings.ylim[0];
+            max_y = settings.ylim[1];
+            gap_y = 0;
+        }
+
+        // Fixed ratio
+        if (settings.fixed) {
+          if (settings.xlim === null && settings.ylim === null) {
+            min_x = min_y = Math.min(min_x, min_y);
+            max_x = max_y = Math.max(max_x, max_y);
+            gap_x = gap_y = Math.max(gap_x, gap_y);
+          }
+          if (settings.xlim !== null) {
+            min_y = min_x;
+            max_y = max_x;
+            gap_y = gap_x;
+          }
+          if (settings.ylim !== null) {
+            min_x = min_y;
+            max_x = max_y;
+            gap_x = gap_y;
+          }
+
+        }
+
+        // x, y, color, symbol and size scales
+        x = d3.scale.linear().range([0, dims.width]);
+        y = d3.scale.linear().range([dims.height, 0]);
+        x.domain([min_x - gap_x, max_x + gap_x]);
+        y.domain([min_y - gap_y, max_y + gap_y]);
+        if (settings.colors === null) {
+            // Number of different levels. See https://github.com/mbostock/d3/issues/472
+            var n = d3.map(data, function(d) { return d.col_var; }).size();
+            color_scale = n <= 10 ? d3.scale.category10() : d3.scale.category20();
+        } else if (Array.isArray(settings.colors)) {
+            color_scale = d3.scale.ordinal().range(settings.colors);
+        } else if (typeof(settings.colors) === "object"){
+            color_scale = d3.scale.ordinal()
+                          .range(d3.values(settings.colors))
+                          .domain(d3.keys(settings.colors));
+        }
+        symbol_scale = d3.scale.ordinal().range(d3.range(d3.svg.symbolTypes.length));
+        size_scale = d3.scale.linear()
+        .range(settings.size_range)
+        .domain([d3.min(data, function(d) { return(d.size_var);} ),
+                 d3.max(data, function(d) { return(d.size_var);} )]);
+
+        // zoom behavior
+        zoom = d3.behavior.zoom()
+        .x(x)
+        .y(y)
+        .scaleExtent([0, 32])
+        .on("zoom", zoomed);
+
+        // x and y axis functions
+        xAxis = d3.svg.axis()
+        .scale(x)
+        .orient("bottom")
+        .tickSize(-dims.height);
+        yAxis = d3.svg.axis()
+        .scale(y)
+        .orient("left")
+        .tickSize(-dims.width);
+
+    }
+
+    // Key function to identify rows when interactively filtering
+    function key(d) {
+        return d.key_var;
+    }
+
+    // Default translation function for points and labels
+    function translation(d) {
+        return "translate(" + x(d.x) + "," + y(d.y) + ")";
+    }
+
+    // Zoom function
+    function zoomed(reset) {
+        svg.select(".x.axis").call(xAxis);
+        svg.select(".y.axis").call(yAxis);
+        svg.selectAll(".dot, .point-label")
+        .attr("transform", translation);
+        svg.selectAll(".arrow").call(draw_arrow);
+        svg.selectAll(".ellipse").call(ellipse_formatting);
+        var zeroline = d3.svg.line()
+        .x(function(d) {return x(d.x)})
+        .y(function(d) {return y(d.y)});
+        svg.select(".zeroline.hline").attr("d", zeroline([{x:x.domain()[0], y:0}, {x:x.domain()[1], y:0}]));
+        svg.select(".zeroline.vline").attr("d", zeroline([{x:0, y:y.domain()[0]}, {x:0, y:y.domain()[1]}]));
+        svg.select(".unit-circle").call(unit_circle_init);
+
+    }
+
+    // Create and draw x and y axes
+    function add_axes(selection) {
+
+        // x axis
+        selection.append("g")
+        .attr("class", "x axis")
+        .attr("transform", "translate(0," + dims.height + ")")
+        .call(xAxis)
+        .append("text")
+        .attr("class", "axis-label")
+        .attr("x", dims.width - 5)
+        .attr("y", -6)
+        .style("text-anchor", "end")
+        .text(settings.xlab);
+
+        // y axis
+        selection.append("g")
+        .attr("class", "y axis")
+        .call(yAxis)
+        .append("text")
+        .attr("class", "axis-label")
+        .attr("transform", "rotate(-90)")
+        .attr("x", -5)
+        .attr("y", 6)
+        .attr("dy", ".71em")
+        .style("text-anchor", "end")
+        .text(settings.ylab);
+
+    }
+
+    // Zero horizontal and vertical lines
+    zeroline = d3.svg.line()
+    .x(function(d) {return x(d.x)})
+    .y(function(d) {return y(d.y)});
+
+    // Create tooltip content function
+    function tooltip_content(d) {
+        // no tooltips
+        if (!settings.has_tooltips) return null;
+        if (settings.has_custom_tooltips) {
+            // custom tooltips
+            return d.tooltip_text;
+        } else {
+            // default tooltips
+            var text = Array();
+            if (settings.has_labels) text.push("<b>"+d.lab+"</b>");
+            text.push("<b>"+settings.xlab+":</b> "+d.x.toFixed(3));
+            text.push("<b>"+settings.ylab+":</b> "+d.y.toFixed(3));
+            if (settings.has_color_var) text.push("<b>"+settings.col_lab+":</b> "+d.col_var);
+            if (settings.has_symbol_var) text.push("<b>"+settings.symbol_lab+":</b> "+d.symbol_var);
+            if (settings.has_size_var) text.push("<b>"+settings.size_lab+":</b> "+d.size_var);
+            return text.join("<br />");
+        }
+    }
+
+    // Clean variables levels to be valid CSS classes
+    function css_clean(s) {
+      if (s === undefined) return "";
+      return s.toString().replace(/[^\w-]/g, "_");
+    }
+
+    // Initial dot attributes
+    function dot_init (selection) {
+         // tooltips when hovering points
+        if (settings.has_tooltips) {
+            var tooltip = d3.select(".scatterD3-tooltip");
+            selection.on("mouseover", function(d, i){
+                tooltip.style("visibility", "visible")
+                .html(tooltip_content(d));
+            });
+            selection.on("mousemove", function(){
+                tooltip.style("top", (d3.event.pageY+15)+"px").style("left",(d3.event.pageX+15)+"px");
+            });
+            selection.on("mouseout", function(){
+                tooltip.style("visibility", "hidden");
+            });
+        }
+    }
+
+    // Apply format to dot
+    function dot_formatting(selection) {
+        var sel = selection
+        .attr("transform", translation)
+        // fill color
+        .style("fill", function(d) { return color_scale(d.col_var); })
+        // symbol and size
+        .attr("d", d3.svg.symbol()
+            .type(function(d) {return d3.svg.symbolTypes[symbol_scale(d.symbol_var)]})
+            .size(function(d) {
+                if (settings.has_size_var) { return size_scale(d.size_var)}
+                else { return settings.point_size }
+            })
+        )
+        .attr("class", function(d,i) {
+          return "dot symbol symbol-c" + css_clean(d.symbol_var) + " color color-c" + css_clean(d.col_var);
+        })
+        if (settings.opacity_changed || settings.subset_changed || settings.redraw) {
+            sel = sel
+            .style("opacity", function(d) {return d.point_opacity});
+        }
+        return sel;
+    }
+
+    // Arrow drawing function
+    function draw_arrow(selection) {
+        selection
+        .attr("x1", function(d) { return x(0) })
+        .attr("y1", function(d) { return y(0) })
+        .attr("x2", function(d) { return x(d.x) })
+        .attr("y2", function(d) { return y(d.y) });
+    }
+
+    // Initial arrow attributes
+    function arrow_init (selection) {
+        selection
+         // tooltips when hovering points
+        if (settings.has_tooltips) {
+            var tooltip = d3.select(".scatterD3-tooltip");
+            selection.on("mouseover", function(d, i){
+                tooltip.style("visibility", "visible")
+                .html(tooltip_content(d));
+            });
+            selection.on("mousemove", function(){
+                tooltip.style("top", (d3.event.pageY+15)+"px").style("left",(d3.event.pageX+15)+"px");
+            });
+            selection.on("mouseout", function(){
+                tooltip.style("visibility", "hidden");
+            });
+        }
+    }
+
+    // Apply format to arrow
+    function arrow_formatting(selection) {
+        var sel = selection
+        .call(draw_arrow)
+        .style("stroke-width", "1px")
+        // stroke color
+        .style("stroke", function(d) { return color_scale(d.col_var); })
+        .attr("marker-end", function(d) { return "url(#arrow-head-" + settings.html_id + "-" + color_scale(d.col_var) + ")" })
+        .attr("class", function(d,i) { return "arrow color color-c" + css_clean(d.col_var) });
+        if (settings.opacity_changed || settings.subset_changed || settings.redraw) {
+            sel = sel.style("opacity", function(d) {return d.point_opacity});
+        }
+        return sel;
+    }
+
+    // Initial ellipse attributes
+    function ellipse_init(selection) {
+        selection
+        .style("fill", "none");
+    }
+
+    // Apply format to ellipse
+    function ellipse_formatting(selection) {
+
+        // Ellipses path function
+        var ellipseFunc = d3.svg.line()
+        .x(function(d) { return x(d.x); })
+        .y(function(d) { return y(d.y); });
+
+        selection
+        .attr("d", function(d) {
+          var ell = HTMLWidgets.dataframeToD3(d.data);
+          return (ellipseFunc(ell))
+        })
+        .style("stroke", function(d) {
+            // Only one ellipse
+            if (d.level == "_scatterD3_all") {
+                return(color_scale.range()[0]);
+            }
+            return( color_scale(d.level))
+        })
+        .style("opacity", 1)
+        .attr("class", function(d) {
+            return "ellipse color color-c" + css_clean(d.level);
+        });
+    }
+
+    // Unit circle init
+    function unit_circle_init(selection) {
+        selection
+        .attr('cx', x(0))
+        .attr('cy', y(0))
+        .attr('rx', x(1)-x(0))
+        .attr('ry', y(0)-y(1))
+        .style("stroke", "#888")
+        .style("fill", "none")
+        .style("opacity", "1");
+    }
+
+    // Initial text label attributes
+    function label_init (selection) {
+        selection
+        .attr("text-anchor", "middle");
+    }
+
+    // Compute default vertical offset for labels
+    function default_label_dy(size, y, type_var) {
+        if (y < 0 && type_var !== undefined && type_var == "arrow") {
+          return (Math.sqrt(size) / 2) + settings.labels_size + 2;
+        }
+        else {
+          return (-Math.sqrt(size) / 2) - 6;
+        }
+    }
+
+    // Apply format to text label
+    function label_formatting (selection) {
+        var sel = selection
+        .text(function(d) {return(d.lab)})
+        .style("font-size", settings.labels_size + "px")
+        .attr("class", function(d,i) { return "point-label color color-c" + css_clean(d.col_var) + " symbol symbol-c" + css_clean(d.symbol_var); })
+        .attr("transform", translation)
+        .style("fill", function(d) { return color_scale(d.col_var); })
+        .attr("dx", function(d) {
+            if (d.lab_dx === undefined) return("0px");
+            else return(d.lab_dx + "px");
+        })
+        .attr("dy", function(d) {
+            if (d.lab_dy !== undefined) return(d.lab_dy + "px");
+            var size = (d.size_var === undefined) ? settings.point_size : size_scale(d.size_var);
+            return default_label_dy(size, d.y, d.type_var) + "px";
+        });
+        if (settings.opacity_changed || settings.subset_changed || settings.redraw) {
+            sel = sel.style("opacity", 1);
+        }
+        return sel;
+    }
+
+    // Text labels dragging function
+    var dragging = false;
+    drag = d3.behavior.drag()
+    .origin(function(d) {
+        var size = (d.size_var === undefined) ? settings.point_size : size_scale(d.size_var);
+        var dx = (d.lab_dx === undefined) ? 0 : d.lab_dx;
+        var dy = (d.lab_dx === undefined) ? default_label_dy(size, d.y, d.type_var) : d.lab_dy;
+        return {x:x(d.x)+dx, y:y(d.y)+dy};
+    })
+    .on('dragstart', function(d) {
+      if (!d3.event.sourceEvent.shiftKey) {
+        dragging = true;
+        d3.select(this).style('fill', '#000');
+        var chart = d3.select(this).node().parentNode;
+        var size = (d.size_var === undefined) ? settings.point_size : size_scale(d.size_var);
+        var dx = (d.lab_dx === undefined) ? 0 : d.lab_dx;
+        var dy = (d.lab_dx === undefined) ? default_label_dy(size, d.y, d.type_var) : d.lab_dy;
+        d3.select(chart).append("svg:line")
+        .attr("id", "scatterD3-drag-line")
+        .attr("x1", x(d.x)).attr("x2", x(d.x) + dx)
+        .attr("y1", y(d.y)).attr("y2", y(d.y) + dy)
+        .style("stroke", "#000")
+        .style("opacity", 0.3);
+      }
+    })
+    .on('drag', function(d) {
+      if (dragging) {
+        cx = d3.event.x - x(d.x);
+        cy = d3.event.y - y(d.y);
+        d3.select(this)
+        .attr('dx', cx + "px")
+        .attr('dy', cy + "px");
+        d3.select("#scatterD3-drag-line")
+        .attr('x2', x(d.x) + cx)
+        .attr("y2", y(d.y) + cy);
+        d.lab_dx = cx;
+        d.lab_dy = cy;
+      }
+    })
+    .on('dragend', function(d) {
+      if (dragging){
+        d3.select(this).style('fill', color_scale(d.col_var));
+        d3.select("#scatterD3-drag-line").remove();
+        dragging = false;
+      }
+    });
+
+
+
+    // Lasso functions to execute while lassoing
+    lasso_start = function() {
+        lasso.items()
+        .each(function(d){
+            if (d3.select(this).classed('dot')) {
+                d.scatterD3_lasso_dot_stroke = d.scatterD3_lasso_dot_stroke ? d.scatterD3_lasso_dot_stroke : d3.select(this).style("stroke");
+                d.scatterD3_lasso_dot_fill = d.scatterD3_lasso_dot_fill ? d.scatterD3_lasso_dot_fill : d3.select(this).style("fill");
+                d.scatterD3_lasso_dot_opacity = d.scatterD3_lasso_dot_opacity ? d.scatterD3_lasso_dot_opacity : d3.select(this).style("opacity");
+            }
+            if (d3.select(this).classed('arrow')) {
+                d.scatterD3_lasso_arrow_stroke = d.scatterD3_lasso_arrow_stroke ? d.scatterD3_lasso_arrow_stroke : d3.select(this).style("stroke");
+                d.scatterD3_lasso_arrow_fill = d.scatterD3_lasso_arrow_fill ? d.scatterD3_lasso_arrow_fill : d3.select(this).style("fill");
+                d.scatterD3_lasso_arrow_opacity = d.scatterD3_lasso_arrow_opacity ? d.scatterD3_lasso_arrow_opacity : d3.select(this).style("opacity");
+            }
+            if (d3.select(this).classed('point-label')) {
+                d.scatterD3_lasso_text_stroke = d.scatterD3_lasso_text_stroke ? d.scatterD3_lasso_text_stroke : d3.select(this).style("stroke");
+                d.scatterD3_lasso_text_fill = d.scatterD3_lasso_text_fill ? d.scatterD3_lasso_text_fill : d3.select(this).style("fill");
+                d.scatterD3_lasso_text_opacity = d.scatterD3_lasso_text_opacity ? d.scatterD3_lasso_text_opacity : d3.select(this).style("opacity");
+            }
+        })
+        .style("fill", null) // clear all of the fills
+        .style("opacity", null) // clear all of the opacities
+        .style("stroke", null) // clear all of the strokes
+        .classed({"not-possible-lasso": true, "selected-lasso": false, "not-selected-lasso": false}); // style as not possible
+    };
+    lasso_draw = function() {
+        // Style the possible dots
+        lasso.items().filter(function(d) {return d.possible === true})
+        .classed({"not-possible-lasso": false, "possible-lasso": true});
+        // Style the not possible dot
+        lasso.items().filter(function(d) {return d.possible === false})
+        .classed({"not-possible-lasso": true, "possible-lasso": false});
+    };
+    lasso_end = function() {
+        lasso_off(svg);
+        d3.select("#" + settings.dom_id_lasso_toggle).classed("active", false);
+        var some_selected = false;
+        if(lasso.items().filter(function(d) {return d.selected === true})[0].length !== 0){
+            some_selected = true;
+        }
+        // Reset the color of all dots
+        lasso.items()
+           .style("fill", function(d) {
+               if (d3.select(this).classed('point-label')) { return d.scatterD3_lasso_text_fill; }
+               if (d3.select(this).classed('dot')) { return d.scatterD3_lasso_dot_fill; }
+               if (d3.select(this).classed('arrow')) { return d.scatterD3_lasso_arrow_fill; }
+           })
+           .style("opacity", function(d) {
+               if (d3.select(this).classed('point-label')) { return d.scatterD3_lasso_text_opacity; }
+               if (d3.select(this).classed('dot')) { return d.scatterD3_lasso_dot_opacity; }
+               if (d3.select(this).classed('arrow')) { return d.scatterD3_lasso_arrow_opacity; }
+           })
+           .style("stroke", function(d) {
+               if (d3.select(this).classed('point-label')) { return d.scatterD3_lasso_text_stroke; }
+               if (d3.select(this).classed('dot')) { return d.scatterD3_lasso_dot_stroke; }
+               if (d3.select(this).classed('arrow')) { return d.scatterD3_lasso_arrow_stroke; }
+           });
+        if (some_selected) {
+          // Style the selected dots
+          var sel = lasso.items().filter(function(d) {return d.selected === true})
+            .classed({"not-possible-lasso": false, "possible-lasso": false, "selected-lasso": true})
+            .style("opacity", "1");
+
+          // Reset the style of the not selected dots
+          lasso.items().filter(function(d) {return d.selected === false})
+            .classed({"not-possible-lasso": false, "possible-lasso": false, "not-selected-lasso": true})
+            .style("opacity", function(d) { return d.point_opacity / 7 });
+
+          // Call custom callback function
+          var callback_sel = svg.selectAll(".dot, .arrow").filter(function(d) {return d.selected === true});
+          if (typeof settings.lasso_callback === 'function') settings.lasso_callback(callback_sel);
+        }
+        else {
+          lasso.items()
+            .classed({"not-possible-lasso": false, "possible-lasso": false,
+                      "not-selected-lasso": false, "selected-lasso": false})
+            .style("opacity", function(d) {
+                if (d3.select(this).classed('point-label')) {return 1};
+                return d.point_opacity;
+            })
+        }
+      };
+      lasso_classes = ".dot, .arrow, .point-label";
+      // Define the lasso
+      lasso_base = d3.lasso()
+      .closePathDistance(2000)   // max distance for the lasso loop to be closed
+      .closePathSelect(true)     // can items be selected by closing the path?
+      .hoverSelect(true)         // can items by selected by hovering over them?
+      .on("start", lasso_start)   // lasso start function
+      .on("draw", lasso_draw)     // lasso draw function
+      .on("end", lasso_end);      // lasso end function
+
+    // Toggle lasso on / zoom off
+    function lasso_on(svg) {
+        var pane = svg.select(".pane");
+        var chart_body = svg.select(".chart-body");
+        // Disable zoom behavior
+        pane.on(".zoom", null);
+        // Enable lasso
+        lasso = lasso_base
+        .area(pane)
+        .items(chart_body.selectAll(lasso_classes));
+        chart_body.call(lasso);
+        // Change cursor style
+        pane.style("cursor", "crosshair");
+    }
+
+    // Toggle lasso off / zoom on
+    function lasso_off(svg) {
+        var pane = svg.select(".pane");
+        // Disable lasso
+        pane.on(".dragstart", null);
+        pane.on(".drag", null);
+        pane.on(".dragend", null);
+        // Enable zoom
+        pane.call(zoom);
+        // Change cursor style
+        pane.style("cursor", "move");
+    }
+
+    // Format legend label
+    function legend_label_formatting (selection, margin_top) {
+        selection
+        .style("text-anchor", "beginning")
+        .style("fill", "#000")
+        .style("font-weight", "bold");
+    }
+
+    // Create color legend
+    function add_color_legend() {
+
+        var legend = svg.select(".legend");
+
+        var legend_color_domain = color_scale.domain().sort();
+        var legend_color_scale = d3.scale.category10();
+
+        legend_color_scale
+        .domain(legend_color_domain)
+        .range(legend_color_domain.map(function(d) {return color_scale(d)}));
+
+        var color_legend = d3.legend.color()
+        .shapePadding(3)
+        .shape("rect")
+        .scale(legend_color_scale)
+        .on("cellover", function(d) {
+            d = css_clean(d);
+            var nsel = ".color:not(.color-c" + d + "):not(.selected-lasso):not(.not-selected-lasso)";
+            var sel = ".color-c" + d + ":not(.selected-lasso):not(.not-selected-lasso)";
+            svg.selectAll(nsel)
+            .transition()
+            .style("opacity", 0.2);
+            svg.selectAll(sel)
+            .transition()
+            .style("opacity", 1);
+        })
+        .on("cellout", function(d) {
+            var sel = ".color:not(.selected-lasso):not(.not-selected-lasso)";
+            svg.selectAll(sel)
+            .transition()
+            .style("opacity", function(d2) {return d2.point_opacity});
+            svg.selectAll(".point-label:not(.selected-lasso):not(.not-selected-lasso)")
+            .transition()
+            .style("opacity", 1);
+        });
+
+        legend.append("g")
+        .append("text")
+        .attr("class", "color-legend-label")
+        .attr("transform", "translate(" + dims.legend_x + "," + margin.legend_top + ")")
+        .text(settings.col_lab)
+        .call(legend_label_formatting);
+
+        legend.append("g")
+        .attr("class", "color-legend")
+        .attr("transform", "translate(" + dims.legend_x + "," + (margin.legend_top + 8) + ")")
+        .call(color_legend);
+    }
+
+    // Create symbol legend
+    function add_symbol_legend() {
+
+        var legend = svg.select(".legend");
+
+        // Height of color legend
+        var color_legend_height = settings.has_color_var ? color_scale.domain().length * 20 + 30 : 0;
+        margin.symbol_legend_top = color_legend_height + margin.legend_top;
+
+        var legend_symbol_domain = symbol_scale.domain().sort();
+        var legend_symbol_scale = d3.scale.ordinal()
+        .domain(legend_symbol_domain)
+        .range(legend_symbol_domain.map(function(d) {return d3.svg.symbol().type(d3.svg.symbolTypes[symbol_scale(d)])()}));
+
+        var symbol_legend = d3.legend.symbol()
+        .shapePadding(5)
+        .scale(legend_symbol_scale)
+        .on("cellover", function(d) {
+            d = css_clean(d);
+            var nsel = ".symbol:not(.symbol-c" + d + "):not(.selected-lasso):not(.not-selected-lasso)";
+            var sel = ".symbol-c" + d + ":not(.selected-lasso):not(.not-selected-lasso)";
+            svg.selectAll(nsel)
+            .transition()
+            .style("opacity", 0.2);
+            svg.selectAll(sel)
+            .transition()
+            .style("opacity", 1);
+        })
+        .on("cellout", function(d) {
+            var sel = ".symbol:not(.selected-lasso):not(.not-selected-lasso)";
+            svg.selectAll(sel)
+            .transition()
+            .style("opacity", function(d2) {return d2.point_opacity});
+            svg.selectAll(".point-label:not(.selected-lasso):not(.not-selected-lasso)")
+            .transition()
+            .style("opacity", 1);
+        });
+
+        legend.append("g")
+        .append("text")
+        .attr("class", "symbol-legend-label")
+        .attr("transform", "translate(" + dims.legend_x + "," + margin.symbol_legend_top + ")")
+        .text(settings.symbol_lab)
+        .call(legend_label_formatting);
+
+        legend.append("g")
+        .attr("class", "symbol-legend")
+        .attr("transform", "translate(" + (dims.legend_x + 8) + "," + (margin.symbol_legend_top + 14) + ")")
+        .call(symbol_legend);
+
+    }
+
+    // Create size legend
+    function add_size_legend() {
+
+        var legend = svg.select(".legend");
+
+        // Height of color and symbol legends
+        var color_legend_height = settings.has_color_var ? color_scale.domain().length * 20 + 30 : 0;
+        var symbol_legend_height = settings.has_symbol_var ? symbol_scale.domain().length * 20 + 30 : 0;
+        margin.size_legend_top = color_legend_height + symbol_legend_height + margin.legend_top;
+
+        var legend_size_scale = d3.scale.linear()
+        .domain(size_scale.domain())
+        // FIXME : find exact formula
+        .range(size_scale.range().map(function(d) {return Math.sqrt(d)/1.8}));
+
+        var size_legend = d3.legend.size()
+        .shapePadding(3)
+        .shape('circle')
+        .scale(legend_size_scale);
+
+        legend.append("g")
+        .append("text")
+        .attr("class", "size-legend-label")
+        .attr("transform", "translate(" + dims.legend_x + "," + margin.size_legend_top + ")")
+        .text(settings.size_lab)
+        .call(legend_label_formatting);
+
+        legend.append("g")
+        .attr("class", "size-legend")
+        .attr("transform", "translate(" + (dims.legend_x + 8) + "," + (margin.size_legend_top + 14) + ")")
+        .call(size_legend);
+
+    }
+
+
+    // Filter points and arrows data
+    function point_filter(d) {
+      return d.type_var === undefined || d.type_var == "point";
+    }
+    function arrow_filter(d) {
+      return d.type_var !== undefined && d.type_var == "arrow";
+    }
+
+
+    function chart(selection) {
+        selection.each(function() {
+
+            setup_sizes();
+            setup_scales();
+
+            // Root chart element and axes
+            root = svg.append("g")
+            .attr("class", "root")
+            .style("fill", "#FFF")
+            .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
+            .call(add_axes);
+
+            // <defs>
+            var defs = svg.append("defs");
+            // clipping rectangle
+            defs.append("clipPath")
+            .attr('id', 'scatterclip-' + settings.html_id)
+            .append('rect')
+            .attr('class', 'cliprect')
+            .attr('width', dims.width)
+            .attr('height', dims.height);
+            // arrow head markers
+            color_scale.range().forEach(function(d) {
+                defs.append("marker")
+                .attr("id", "arrow-head-" + settings.html_id + "-" + d)
+                .attr("markerWidth", "10")
+                .attr("markerHeight", "10")
+                .attr("refX", "10")
+                .attr("refY", "4")
+                .attr("orient", "auto")
+                .append("path")
+                .attr("d", "M0,0 L0,8 L10,4 L0,0")
+                .style("fill", d);
+            });
+
+            // zoom pane
+            var pane = root.append("rect")
+            .attr("class", "pane")
+            .attr("width", dims.width)
+            .attr("height", dims.height)
+            .style("fill", "none")
+            .style("pointer-events", "all")
+            .call(zoom);
+
+            // chart body
+            var clip_path_url = document.location.href.replace(/#[^?]+?(\?|$)/, "$1") +
+                                "#scatterclip-" + settings.html_id
+            var chart_body = root.append("g")
+            .attr("class", "chart-body")
+            .attr("width", dims.width)
+            .attr("height", dims.height)
+            .attr("clip-path", "url(" + clip_path_url + ")");
+
+             chart_body.append("path")
+            .attr("class", "zeroline hline")
+            .attr("d", zeroline([{x:x.domain()[0], y:0}, {x:x.domain()[1], y:0}]));
+            chart_body.append("path")
+            .attr("class", "zeroline vline")
+            .attr("d", zeroline([{x:0, y:y.domain()[0]}, {x:0, y:y.domain()[1]}]));
+
+            // Unit circle
+            if (settings.unit_circle) {
+              var unit_circle = chart_body.append('svg:ellipse')
+              .attr('class', 'unit-circle')
+              .call(unit_circle_init);
+            }
+
+            // Add points
+            var dot = chart_body
+            .selectAll(".dot")
+            .data(data.filter(point_filter), key);
+            dot.enter()
+            .append("path")
+            .call(dot_init)
+            .call(dot_formatting);
+            // Add arrows
+            var arrow = chart_body
+            .selectAll(".arrow")
+            .data(data.filter(arrow_filter), key);
+            arrow.enter()
+            .append("svg:line")
+            .call(arrow_init)
+            .call(arrow_formatting);
+
+            // Add ellipses
+            if (settings.ellipses) {
+              var ellipse = chart_body
+              .selectAll(".ellipse")
+              .data(settings.ellipses_data);
+              ellipse.enter()
+              .append("svg:path")
+              .call(ellipse_init)
+              .call(ellipse_formatting);
+            }
+
+            // Add text labels
+            if (settings.has_labels) {
+                var labels = chart_body.selectAll(".point-label")
+                .data(data, key);
+
+                labels.enter()
+                .append("text")
+                .call(label_init)
+                .call(label_formatting)
+                .call(drag);
+            }
+
+            // Legends
+            if (settings.has_legend && settings.legend_width > 0) {
+                var legend = svg.append("g").attr("class", "legend");
+                // Color legend
+                if (settings.has_color_var) {
+                    add_color_legend.svg = svg;
+                    add_color_legend(legend);
+                }
+                // Symbol legend
+                if (settings.has_symbol_var) {
+                    add_symbol_legend.svg = svg;
+                    add_symbol_legend(legend);
+                }
+                // Size legend
+                if (settings.has_size_var) add_size_legend(legend);
+            }
+
+        });
+    }
+
+
+    // Update chart with transitions
+    function update_settings(old_settings) {
+        if (old_settings.labels_size != settings.labels_size)
+            svg.selectAll(".point-label").transition().style("font-size", settings.labels_size + "px");
+        if (old_settings.point_size != settings.point_size)
+            svg.selectAll(".dot").transition().call(dot_formatting);
+        if (old_settings.has_labels != settings.has_labels) {
+            if (!settings.has_labels) {
+                svg.selectAll(".point-label").remove();
+            }
+            if (settings.has_labels) {
+                var chart_body = svg.select(".chart-body");
+                var labels = chart_body.selectAll(".point-label")
+                            .data(data, key);
+                labels.enter()
+                .append("text")
+                .call(label_init)
+                .call(label_formatting)
+                .call(drag);
+            }
+        }
+        if (old_settings.unit_circle != settings.unit_circle) {
+            if (!settings.unit_circle) {
+                var circle = svg.select(".unit-circle");
+                circle.transition().duration(1000).call(unit_circle_init)
+                .style("opacity", "0").remove();
+            }
+            if (settings.unit_circle) {
+                var chart_body = svg.select(".chart-body");
+                chart_body.append('svg:ellipse')
+                .attr('class', 'unit-circle')
+                .style("opacity", "0");
+            }
+        }
+    };
+
+    // Update data with transitions
+    function update_data() {
+
+      if (settings.has_legend_changed && settings.legend_width > 0)
+            resize_chart();
+
+      setup_sizes();
+      setup_scales();
+
+      var t0 = svg.transition().duration(1000);
+      svg.select(".x.axis .axis-label").text(settings.xlab);
+      t0.select(".x.axis").call(xAxis);
+      t0.select(".zeroline.vline").attr("d", zeroline([{x:0, y:y.domain()[0]}, {x:0, y:y.domain()[1]}]));
+      svg.select(".y.axis .axis-label").text(settings.ylab);
+      t0.select(".y.axis").call(yAxis);
+      t0.select(".zeroline.hline").attr("d", zeroline([{x:x.domain()[0], y:0}, {x:x.domain()[1], y:0}]));
+      svg.select(".pane").call(zoom);
+      zoom.x(x);
+      zoom.y(y);
+      // Unit circle
+      if (settings.unit_circle) t0.select(".unit-circle").call(unit_circle_init);
+
+      var chart_body = svg.select(".chart-body");
+
+      // Add points
+      var dot = chart_body
+      .selectAll(".dot")
+      .data(data.filter(point_filter), key);
+      dot.enter().append("path").call(dot_init);
+      dot.transition().duration(1000).call(dot_formatting);
+      dot.exit().transition().duration(1000).attr("transform", "translate(0,0)").remove();
+      // Add arrows
+      var arrow = chart_body
+      .selectAll(".arrow")
+      .data(data.filter(arrow_filter), key);
+      arrow.enter().append("svg:line").call(arrow_init)
+      .style("opacity", "0")
+      .transition().duration(1000)
+      .style("opacity", "1");
+      arrow.transition().duration(1000).call(arrow_formatting);
+      arrow.exit().transition().duration(1000).style("opacity", "0").remove();
+
+      // Add ellipses
+      if (settings.ellipses || settings.ellipses_changed) {
+          var ellipse = chart_body
+          .selectAll(".ellipse")
+          .data(settings.ellipses_data);
+          ellipse.enter().append("path").call(ellipse_init)
+          .style("opacity", "0")
+          .transition().duration(1000)
+          .style("opacity", "1");
+          ellipse.transition().duration(1000).call(ellipse_formatting);
+          ellipse.exit().transition().duration(1000).style("opacity", "0").remove();
+      }
+
+      if (settings.has_labels) {
+          var labels = chart_body.selectAll(".point-label")
+          .data(data, key);
+          labels.enter().append("text").call(label_init).call(drag);
+          labels.transition().duration(1000).call(label_formatting);
+          labels.exit().transition().duration(1000).attr("transform", "translate(0,0)").remove();
+      }
+
+      if (settings.legend_changed) {
+
+          // Remove existing legends
+          svg.select(".legend").remove();
+          var legend = svg.append("g").attr("class", "legend");
+
+          // Recreate them
+          if (settings.has_legend && settings.legend_width > 0) {
+              // Color legend
+              if (settings.has_color_var) {
+                add_color_legend.svg = svg;
+                add_color_legend(legend);
+              }
+              // Symbol legend
+              if (settings.has_symbol_var) {
+                add_symbol_legend.svg = svg;
+                add_symbol_legend(legend);
+              }
+              // Size legend
+              if (settings.has_size_var) add_size_legend(legend);
+          }
+      }
+
+      lasso_off(svg);
+      d3.select("#" + settings.dom_id_lasso_toggle).classed("active", false);
+    };
+
+    // Dynamically resize chart elements
+    function resize_chart () {
+        // recompute sizes
+        setup_sizes();
+        // recompute scales and zoom
+        var cache_translate = zoom.translate();
+        var cache_scale = zoom.scale();
+        zoom.scale(1).translate([0, 0]);
+        x.range([0, dims.width]);
+        y.range([dims.height, 0]);
+        xAxis.scale(x).tickSize(-dims.height);
+        yAxis.scale(y).tickSize(-dims.width);
+        zoom.x(x);
+        zoom.y(y);
+        zoom.translate(cache_translate);
+        zoom.scale(cache_scale);
+        // Change svg attributes
+        svg.select(".root").attr("width", dims.width).attr("height", dims.height);
+        svg.select(".cliprect").attr("width", dims.width).attr("height", dims.height);
+        svg.select(".pane").attr("width", dims.width).attr("height", dims.height).call(zoom);
+        svg.select(".chart-body").attr("width", dims.width).attr("height", dims.height);
+        svg.select(".x.axis").attr("transform", "translate(0," + dims.height + ")").call(xAxis);
+        svg.select(".x.axis .axis-label").attr("x", dims.width - 5);
+        svg.select(".y.axis").call(yAxis);
+        svg.select(".unit-circle").call(unit_circle_init);
+
+        svg.selectAll(".dot").attr("transform", translation);
+        svg.selectAll(".arrow").call(draw_arrow);
+        svg.selectAll(".ellipse").call(ellipse_formatting);
+        if (settings.has_labels) {
+            svg.selectAll(".point-label")
+            .attr("transform", translation);
+        }
+        // Move zerolines
+        var zeroline = d3.svg.line()
+        .x(function(d) {return x(d.x)})
+        .y(function(d) {return y(d.y)});
+        svg.select(".zeroline.hline").attr("d", zeroline([{x:x.domain()[0], y:0}, {x:x.domain()[1], y:0}]));
+        svg.select(".zeroline.vline").attr("d", zeroline([{x:0, y:y.domain()[0]}, {x:0, y:y.domain()[1]}]));
+        // Move legends
+        if (settings.has_color_var) {
+            svg.select(".color-legend-label")
+            .attr("transform", "translate(" + dims.legend_x + "," + margin.legend_top + ")");
+            svg.select(".color-legend")
+            .attr("transform", "translate(" + dims.legend_x + "," + (margin.legend_top + 12) + ")");
+        }
+        if (settings.has_symbol_var) {
+            svg.select(".symbol-legend-label")
+            .attr("transform", "translate(" + dims.legend_x + "," + margin.symbol_legend_top + ")");
+            svg.select(".symbol-legend")
+            .attr("transform", "translate(" + (dims.legend_x + 8) + "," + (margin.symbol_legend_top + 14) + ")");
+        }
+        if (settings.has_size_var) {
+            svg.select(".size-legend-label")
+            .attr("transform", "translate(" + dims.legend_x + "," + margin.size_legend_top + ")");
+            svg.select(".size-legend")
+            .attr("transform", "translate(" + (dims.legend_x + 8) + "," + (margin.size_legend_top + 14) + ")");
+        }
+
+    };
+
+    // Add controls handlers for shiny
+    chart.add_controls_handlers = function() {
+
+        // Zoom reset
+        d3.select("#" + settings.dom_id_reset_zoom)
+        .on("click", function() {
+            d3.transition().duration(750).tween("zoom", function() {
+                var ix = d3.interpolate(x.domain(), [min_x - gap_x, max_x + gap_x]),
+                iy = d3.interpolate(y.domain(), [min_y - gap_y, max_y + gap_y]);
+                return function(t) {
+                    zoom.x(x.domain(ix(t))).y(y.domain(iy(t)));
+                    zoomed(reset = true);
+                };
+            })
+        });
+
+        // SVG export
+        d3.select("#" + settings.dom_id_svg_export)
+        .on("click", function(){
+            var svg_content = svg
+            .attr("xmlns", "http://www.w3.org/2000/svg")
+            .attr("version", 1.1)
+            .node().parentNode.innerHTML;
+            svg_content = svg_content.replace(/clip-path="url\(.*?(#.*?)\)"/,
+                                              'clip-path="url($1)"');
+            var imageUrl = "data:image/octet-stream;base64,\n" + btoa(svg_content);
+            d3.select(this)
+            .attr("download", "scatterD3.svg")
+            .attr("href", imageUrl);
+        });
+
+        // Lasso toggle
+        d3.select("#" + settings.dom_id_lasso_toggle)
+        .on("click", function(){
+          if (!d3.select(this).classed("active") && settings.lasso) {
+              lasso_on(svg);
+          }
+          if (d3.select(this).classed("active") && settings.lasso) {
+              lasso_off(svg);
+          }
+        })
+    };
+
+    chart.add_global_listeners = function() {
+      // Toogle zoom and lasso behaviors when shift is pressed
+      var parent = d3.select("#scatterD3-svg-" + settings.html_id).node().parentNode;
+      d3.select(parent)
+      .attr("tabindex", 0)
+      .on("keydown", function() {
+        if (d3.event.keyIdentifier == "Shift") {
+          if (settings.lasso) {
+            lasso_on(svg);
+          }
+        }
+      })
+      .on("keyup", function() {
+        if (d3.event.keyIdentifier == "Shift") {
+          if (settings.lasso) {
+            lasso_off(svg);
+          }
+        }
+      })
+
+    }
+
+    // resize
+    chart.resize = function() {
+        resize_chart();
+    }
+
+    // settings getter/setter
+    chart.data = function(value, redraw) {
+        if (!arguments.length) return data;
+        data = value;
+        if (!redraw) update_data();
+        return chart;
+    };
+
+    // settings getter/setter
+    chart.settings = function(value) {
+        if (!arguments.length) return settings;
+        if (Object.keys(settings).length === 0) {
+            settings = value;
+            // update dims and scales
+            setup_sizes();
+            setup_scales();
+        } else {
+            var old_settings = settings;
+            settings = value;
+            update_settings(old_settings);
+        }
+        return chart;
+    };
+
+    chart.svg = function(value) {
+        if (!arguments.length) return svg;
+        svg = value;
+        return chart;
+    }
+
+    // width getter/setter
+    chart.width = function(value) {
+        if (!arguments.length) return width;
+        width = value;
+        return chart;
+    };
+
+    // height getter/setter
+    chart.height = function(value) {
+        if (!arguments.length) return height;
+        height = value;
+        return chart;
+    };
+
+    return chart;
+}
+
+
+
+HTMLWidgets.widget({
+
+    name: 'scatterD3',
+
+    type: 'output',
+
+    initialize: function(el, width, height) {
+
+        if (width < 0) width = 0;
+        if (height < 0) height = 0;
+        // Create root svg element
+        var svg = d3.select(el).append("svg");
+        svg
+        .attr("width", width)
+        .attr("height", height)
+        .attr("class", "scatterD3")
+        .append("style")
+        .text(".scatterD3 {font: 10px sans-serif;}" +
+        ".scatterD3 .axis line, .axis path { stroke: #000; fill: none; shape-rendering: CrispEdges;} " +
+        ".scatterD3 .axis .tick line { stroke: #ddd;} " +
+        ".scatterD3 .axis text { fill: #000; } " +
+        ".scatterD3 .zeroline { stroke-width: 1; stroke: #444; stroke-dasharray: 5,5;} ");
+
+        // Create tooltip content div
+        var tooltip = d3.select(".scatterD3-tooltip");
+        if (tooltip.empty()) {
+            tooltip = d3.select("body")
+            .append("div")
+            .style("visibility", "hidden")
+            .attr("class", "scatterD3-tooltip");
+        }
+
+        // Create scatterD3 instance
+        return scatterD3().width(width).height(height).svg(svg);
+    },
+
+    resize: function(el, width, height, scatter) {
+
+        if (width < 0) width = 0;
+        if (height < 0) height = 0;
+        // resize root svg element
+        var svg = d3.select(el).select("svg");
+        svg
+        .attr("width", width)
+        .attr("height", height);
+        // resize chart
+        scatter.width(width).height(height).svg(svg).resize();
+    },
+
+    renderValue: function(el, obj, scatter) {
+        // Check if update or redraw
+        var first_draw = (Object.keys(scatter.settings()).length === 0);
+        var redraw = first_draw || !obj.settings.transitions;
+        var svg = d3.select(el).select("svg").attr("id", "scatterD3-svg-" + obj.settings.html_id);
+        scatter = scatter.svg(svg);
+
+        // convert data to d3 format
+        data = HTMLWidgets.dataframeToD3(obj.data);
+
+        // If no transitions, remove chart and redraw it
+        if (!obj.settings.transitions) {
+            svg.selectAll("*:not(style)").remove();
+        }
+
+        // Complete draw
+        if (redraw) {
+            scatter = scatter.data(data, redraw);
+            obj.settings.redraw = true;
+            scatter = scatter.settings(obj.settings);
+            // add controls handlers and global listeners for shiny apps
+            scatter.add_controls_handlers();
+            scatter.add_global_listeners();
+            // draw chart
+            d3.select(el)
+              .call(scatter);
+        }
+        // Update only
+        else {
+            // Check what did change
+            obj.settings.has_legend_changed = scatter.settings().has_legend != obj.settings.has_legend;
+            obj.settings.has_labels_changed = scatter.settings().has_labels != obj.settings.has_labels;
+            obj.settings.size_range_changed = scatter.settings().size_range != obj.settings.size_range;
+            obj.settings.ellipses_changed = scatter.settings().ellipses != obj.settings.ellipses;
+            function changed(varname) {
+                return obj.settings.hashes[varname] != scatter.settings().hashes[varname];
+            };
+            obj.settings.x_changed = changed("x");
+            obj.settings.y_changed = changed("y");
+            obj.settings.lab_changed = changed("lab");
+            obj.settings.legend_changed = changed("col_var") || changed("symbol_var") ||
+                                          changed("size_var") || obj.settings.size_range_changed;
+            obj.settings.data_changed = obj.settings.x_changed || obj.settings.y_changed ||
+                                        obj.settings.lab_changed || obj.settings.legend_changed ||
+                                        obj.settings.has_labels_changed || changed("ellipses_data") ||
+                                        obj.settings.ellipses_changed;
+            obj.settings.opacity_changed = changed("point_opacity");
+            obj.settings.subset_changed = changed("key_var");
+            scatter = scatter.settings(obj.settings);
+            // Update data only if needed
+            if (obj.settings.data_changed) scatter = scatter.data(data, redraw);
+        }
+    }
+
+});
diff --git a/inst/htmlwidgets/scatterD3.yaml b/inst/htmlwidgets/scatterD3.yaml
new file mode 100644
index 0000000..c8f6be9
--- /dev/null
+++ b/inst/htmlwidgets/scatterD3.yaml
@@ -0,0 +1,13 @@
+dependencies:
+  - name: d3
+    version: 3.5.6
+    src: htmlwidgets/lib
+    script:
+      - d3-3.5.6.min.js
+      - d3-legend.min.js
+    stylesheet: scatterD3.css
+  - name: d3.lasso-plugin
+    version: 1.0.0
+    src: htmlwidgets/lib/d3-lasso-plugin
+    script: lasso.js
+    stylesheet: lasso.css
diff --git a/man/scatterD3-shiny.Rd b/man/scatterD3-shiny.Rd
new file mode 100644
index 0000000..7f5916d
--- /dev/null
+++ b/man/scatterD3-shiny.Rd
@@ -0,0 +1,31 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/scatterD3-Rd.R, R/scatterD3.R
+\name{scatterD3-shiny}
+\alias{renderScatterD3}
+\alias{scatterD3-shiny}
+\alias{scatterD3Output}
+\title{Shiny bindings for scatterD3 widgets}
+\usage{
+scatterD3Output(outputId, width = "100\%", height = "600px")
+
+renderScatterD3(expr, env = parent.frame(), quoted = FALSE)
+}
+\arguments{
+\item{outputId}{output variable to read from}
+
+\item{width, height}{Must be a valid CSS unit (like \code{"100\%"},
+\code{"400px"}, \code{"auto"}) or a number, which will be coerced to a
+string and have \code{"px"} appended.}
+
+\item{expr}{An expression that generates a scatterD3 scatter plot.}
+
+\item{env}{The environment in which to evaluate \code{expr}.}
+
+\item{quoted}{Is \code{expr} a quoted expression (with \code{quote()})? This
+is useful if you want to save an expression in a variable.}
+}
+\description{
+Output and render functions for using scatterD3 widgets within Shiny
+applications and interactive Rmd documents.
+}
+
diff --git a/man/scatterD3.Rd b/man/scatterD3.Rd
new file mode 100644
index 0000000..2518a42
--- /dev/null
+++ b/man/scatterD3.Rd
@@ -0,0 +1,118 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/scatterD3.R
+\name{scatterD3}
+\alias{scatterD3}
+\title{Scatter plot HTML widget}
+\source{
+D3.js was created by Michael Bostock. See \url{http://d3js.org/}
+}
+\usage{
+scatterD3(x, y, lab = NULL, point_size = 64, labels_size = 10,
+  point_opacity = 1, fixed = FALSE, col_var = NULL, colors = NULL,
+  ellipses = FALSE, ellipses_level = 0.95, symbol_var = NULL,
+  size_var = NULL, size_range = c(10, 300), col_lab = NULL,
+  symbol_lab = NULL, size_lab = NULL, key_var = NULL, type_var = NULL,
+  unit_circle = FALSE, tooltips = TRUE, tooltip_text = NULL,
+  xlab = NULL, ylab = NULL, html_id = NULL, width = NULL,
+  height = NULL, legend_width = 150, xlim = NULL, ylim = NULL,
+  dom_id_reset_zoom = "scatterD3-reset-zoom",
+  dom_id_svg_export = "scatterD3-svg-export",
+  dom_id_lasso_toggle = "scatterD3-lasso-toggle", transitions = FALSE,
+  lasso = FALSE, lasso_callback = NULL)
+}
+\arguments{
+\item{x}{numerical vector of x values}
+
+\item{y}{numerical vector of y values}
+
+\item{lab}{optional character vector of text labels}
+
+\item{point_size}{points size. Ignored if size_var is not NULL.}
+
+\item{labels_size}{text labels size}
+
+\item{point_opacity}{points opacity, as an integer (same opacity for all points) or a vector of integers}
+
+\item{fixed}{force a 1:1 aspect ratio}
+
+\item{col_var}{optional vector for points color mapping}
+
+\item{colors}{vector of custom points colors. Colors must be
+defined as an hexadecimal string (eg "#FF0000").  If
+\code{colors} is a named list or vector, then the colors will
+be associated with their name within \code{col_var}.}
+
+\item{ellipses}{draw confidence ellipses for points or the different color mapping groups}
+
+\item{ellipses_level}{confidence level for ellipses (0.95 by default)}
+
+\item{symbol_var}{optional vector for points symbol mapping}
+
+\item{size_var}{optional vector for points size mapping}
+
+\item{size_range}{numeric vector of length 2, giving the minimum and maximum point sizes when mapping with size_var}
+
+\item{col_lab}{color legend title}
+
+\item{symbol_lab}{symbols legend title}
+
+\item{size_lab}{size legend title}
+
+\item{key_var}{optional vector of rows ids. This is passed as a key to d3, and is only added in shiny apps where displayed rows are filtered interactively.}
+
+\item{type_var}{optional vector of points type : "point" for adot (default), "arrow" for an arrow starting from the origin.}
+
+\item{unit_circle}{set tot TRUE to draw a unit circle}
+
+\item{tooltips}{logical value to display tooltips when hovering points}
+
+\item{tooltip_text}{optional character vector of tooltips text}
+
+\item{xlab}{x axis label}
+
+\item{ylab}{y axis label}
+
+\item{html_id}{manually specify an HTML id for the svg root node. A random one is generated by default.}
+
+\item{width}{figure width, computed when displayed}
+
+\item{height}{figure height, computed when displayed}
+
+\item{legend_width}{legend area width, in pixels. Set to 0 to disable legend completely.}
+
+\item{xlim}{numeric vector of length 2, manual x axis limits}
+
+\item{ylim}{numeric vector of length 2, manual y axis limits}
+
+\item{dom_id_reset_zoom}{HTML DOM id of the element to bind the "reset zoom" control to.}
+
+\item{dom_id_svg_export}{HTML DOM id of the element to bind the "svg export" control to.}
+
+\item{dom_id_lasso_toggle}{HTML DOM id of the element to bind the "toggle lasso" control to.}
+
+\item{transitions}{if TRUE, data updates are displayed with smooth transitions, if FALSE the whole chart is redrawn. Only used within shiny apps.}
+
+\item{lasso}{logical value to add {https://github.com/skokenes/D3-Lasso-Plugin}{d3-lasso-plugin} feature}
+
+\item{lasso_callback}{the body of a JavaScript callback function with the argument \code{sel} to be applied to a lasso plugin selection}
+}
+\description{
+Generates an interactive scatter plot based on d3.js.
+Interactive features include zooming, panning, text labels moving, tooltips,
+fading effects in legend. Additional handlers are provided to change label
+size, point opacity or export the figure as an SVG file via HTML form controls.
+}
+\details{
+Interactive scatter plots based on htmlwidgets and d3.js
+}
+\examples{
+scatterD3(x = mtcars$wt, y = mtcars$mpg, lab = rownames(mtcars),
+          col_var = mtcars$cyl, symbol_var = mtcars$am,
+          xlab = "Weight", ylab = "Mpg", col_lab = "Cylinders",
+          symbol_lab = "Manual transmission", html_id = NULL)
+
+}
+\author{
+Julien Barnier <julien.barnier at ens-lyon.fr>
+}
+
diff --git a/vignettes/introduction.Rmd b/vignettes/introduction.Rmd
new file mode 100644
index 0000000..0ccc163
--- /dev/null
+++ b/vignettes/introduction.Rmd
@@ -0,0 +1,177 @@
+---
+title: "Interactive scatterplots with scatterD3"
+author: "Julien Barnier"
+date: "`r Sys.Date()`"
+output: 
+  rmarkdown::html_vignette:
+    fig_width: 5
+    toc: true
+vignette: >
+  %\VignetteIndexEntry{Introduction}
+  %\VignetteEngine{knitr::rmarkdown}
+  %\VignetteEncoding{UTF-8}
+---
+
+The `scatterD3` package provides an HTML widget based on the `htmlwidgets` package and allows to produce interactive scatterplots by using the `d3.js` javascript visualization library.
+
+## Basic scatterplot
+
+Starting with the sample `mtcars` dataset, we can produce a basic scatterplot with the following command :
+
+```{r basic}
+library(scatterD3)
+scatterD3(x = mtcars$wt, y = mtcars$mpg)
+```
+
+This will display a simple visualization with the given variables as `x` and `y` axis. There are several interactive features directly available :
+
+- you can zoom in and out with the mouse wheel while the mouse cursor is on the plot
+- you can pan the plot by dragging with your mouse
+- by hovering over a point, you can display a small tooltip window giving the `x` and `y` values
+
+You can customize the points size with the `point_size` parameter, their opacity with `point_opacity`, and you can force the plot to have a 1:1 fixed aspect ratio with `fixed = TRUE`.
+
+```{r basic_cust}
+scatterD3(x = mtcars$wt, y = mtcars$mpg, point_size = 15, point_opacity = 0.5, fixed = TRUE)
+```
+
+## Point labels
+
+You can add text labels to the points by passing a character vector to the `lab` parameter. Labels size are controlled by the `labels_size` parameter.
+
+```{r labels}
+scatterD3(x = mtcars$wt, y = mtcars$mpg, lab = rownames(mtcars), labels_size = 9)
+```
+
+Note that text labels are fully movable : click and drag a label with your mouse to place it where you want. Custom positions are preserved while zooming/panning.
+
+
+## Mapping colors, symbols and size to variables
+
+By passing vectors to the `col_var` and/or `symbol_var` arguments, you can map points colors and symbols to other variables.
+
+```{r mapping}
+scatterD3(x = mtcars$wt, y = mtcars$mpg, col_var = mtcars$cyl, symbol_var = mtcars$gear)
+```
+
+A legend is then automatically added. You can manually specify its width with the `legend_width` argument. Use `legend_width = 0` to disable it entirely.
+
+Note that when hovering over a legend item with your mouse, the corresponding points are highlighted. Also note that the mapped variables values are automatically added to the default tooltips.
+
+You can also map symbol sizes with a variable with the `size_var` argument. `size_range` allows to customize the sizes range :
+
+```{r map_size}
+scatterD3(x = mtcars$wt, y = mtcars$mpg, col_var = mtcars$cyl, size_var = mtcars$hp, 
+          size_range = c(10,1000), point_opacity = 0.7)
+```
+
+## Axis limits
+
+You can manually specify the `x` or `y` axis limits with the `xlim` and `ylim` arguments :
+
+```{r axis_limits}
+scatterD3(x = mtcars$wt, y = mtcars$mpg, xlim=c(0,10), ylim=c(10,35))
+```
+
+
+
+## Custom axis and legend labels
+
+You can customize the axis and legend labels with `xlab`, `ylab`, `col_lab`, `symbol_lab` and `size_lab` :
+
+```{r cust_labels}
+scatterD3(x = mtcars$wt, y = mtcars$mpg, col_var = mtcars$cyl, symbol_var = mtcars$gear,
+          xlab = "Weight", ylab = "Mpg", col_lab = "Cylinders", symbol_lab = "Gears")
+```
+
+Note that default tooltips are updated accordingly.
+
+
+## Custom tooltips
+
+If the default tooltips don't suit your needs, you can customize them by providing a character vector to the `tooltip_text` argument. This can contain HTML tags for formatting.
+
+```{r cust_tooltips}
+tooltips <- paste("This is an incredible <strong>", rownames(mtcars),"</strong><br />with ", 
+                  mtcars$cyl, "cylinders !")
+scatterD3(x = mtcars$wt, y = mtcars$mpg, tooltip_text = tooltips)
+```
+
+You can also disable tooltips entirely with `tooltips = FALSE`.
+
+## Confidence ellipses
+
+You can draw a confidence ellipse around the points :
+
+```{r ellipses}
+scatterD3(x = mtcars$wt, y = mtcars$mpg, ellipses = TRUE)
+```
+
+Or around the different groups of points defined by `col_var` :
+
+```{r ellipses_col}
+scatterD3(x = mtcars$wt, y = mtcars$mpg, col_var = mtcars$cyl, ellipses = TRUE)
+```
+
+Ellipses are computed by the  `ellipse.default()` function of the [ellipse package](https://cran.r-project.org/package=ellipse). The confidence level can be changed with the `ellipse_level` argument (`0.95` by default).
+
+## Lasso selection tool
+
+Thanks to the [d3-lasso-plugin](https://github.com/skokenes/D3-Lasso-Plugin) integration made by @[timelyportfolio](https://github.com/timelyportfolio), you can select and highlight points with a lasso selection tool. To activate it, just add a `lasso = TRUE` argument. The tool is used by shift-clicking and dragging on the plot area (if it doesn't activate, click on the chart first to give it focus).
+
+```{r lasso}
+scatterD3(x = mtcars$wt, y = mtcars$mpg, lab = rownames(mtcars), lasso = TRUE)
+```
+
+To undo the selection, just shift-click again.
+
+You can specify a custom JavaScript callback function to be called by passing it to the `lasso_callback` argument as a character string. This function should accept a `sel` argument, which is a d3 selection of selected points.
+
+Here is an example which shows an alert with selected point labels :
+
+```{r lasso_callback}
+scatterD3(x = mtcars$wt, y = mtcars$mpg, lab = rownames(mtcars), 
+          lasso = TRUE,
+          lasso_callback = "function(sel) {alert(sel.data().map(function(d) {return d.lab}).join('\\n'));}")
+```
+
+
+## Other options
+
+Finally, and for more specific use cases, you can represent some points as an arrow starting from the origin by using the `type_var` argument, and you can add a unit circle with `unit_circle = TRUE`.
+
+```{r cust_arrows}
+scatterD3(x = c(1, 0.9, 0.7, 0.2, -0.4, -0.5), xlab = "x",
+          y = c(1, 0.1, -0.5, 0.5, -0.6, 0.7), ylab = "y",
+          lab = LETTERS[1:6], type_var = c("point", rep("arrow", 5)),
+          unit_circle = TRUE, fixed = TRUE, xlim = c(-1.2, 1.2))
+```
+
+
+## Shiny integration
+
+### Transitions
+
+Like every R HTML widget, shiny integration is straightforward. But as a D3 widget, `scatterD3` is *updatable* : changes in settings or data can be displayed via smooth transitions instead of a complete chart redraw, which can provide interesting visual clues.
+
+For a small demonstration of these transitions, you can take a look at the
+[sample scatterD3 shiny app](http://data.nozav.org/app/scatterD3/).
+
+Enabling transitions in your shiny app is quite simple, you just have to add the `transitions = TRUE` argument to your `scatterD3` calls in your shiny server code. There's only one warning : if your shiny application may filter on your dataset rows via a form control, then you must provide a `key_var` variable that uniquely and persistently identify your rows.
+
+
+### Additional controls : Reset zoom and SVG export
+
+Furthermore, `scatterD3` provides some additional handlers for two interactive features : SVG export and zoom resetting.
+
+By default, you just have to give the following `id` to the corresponding form controls :
+
+- `#scatterD3-reset-zoom` : reset zoom to default on click
+- `#scatterD3-svg-export` : link to download the currently displayed figure as an SVG file
+
+If you are not happy with these ids, you can specify their names yourself with the arguments `dom_id_svg_export` and `dom_id_reset_zoom`.
+
+### Sample app and source code
+
+The
+[sample scatterD3 shiny app](http://data.nozav.org/app/scatterD3/) allows you to see the different features described here. You can [check its source code on GitHub](https://github.com/juba/scatterD3_shiny_app) for a better understanding of the different arguments.

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/r-cran-scatterd3.git



More information about the debian-med-commit mailing list