diff --git a/.Rbuildignore b/.Rbuildignore new file mode 100644 index 000000000..91114bf2f --- /dev/null +++ b/.Rbuildignore @@ -0,0 +1,2 @@ +^.*\.Rproj$ +^\.Rproj\.user$ diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..684a1a58c --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +.Rproj.user +.Rhistory +.RData +.Ruserdata +*.Rproj +*.httr-oauth +*animint-htmltest* +*intreg-selection* +*ANIMINT_TEST_FOO +*pids.txt diff --git a/DESCRIPTION b/DESCRIPTION new file mode 100644 index 000000000..8af77a3e5 --- /dev/null +++ b/DESCRIPTION @@ -0,0 +1,11 @@ +Package: animint2 +Title: Interactive animations +Version: 2017.06.13 +Authors@R: person("First", "Last", email = "first.last@example.com", role = c("aut", "cre")) +Description: An interactive animation can be defined using a list of + ggplots with clickSelects and showSelected aesthetics. +Depends: R (>= 3.3.2) +License: What license is it under? +Encoding: UTF-8 +LazyData: true +RoxygenNote: 6.0.1 diff --git a/NAMESPACE b/NAMESPACE new file mode 100644 index 000000000..feca1dd0a --- /dev/null +++ b/NAMESPACE @@ -0,0 +1,27 @@ +# Generated by roxygen2: do not edit by hand + +export(animint2dir) +export(animint2gist) +export(animintOutput) +export(geom_tallrect) +export(geom_widerect) +export(getLegendList) +export(is.rgb) +export(knit_print.animint2) +export(make_bar) +export(make_tallrect) +export(make_tallrect_or_widerect) +export(make_text) +export(make_widerect) +export(parsePlot) +export(renderAnimint) +export(saveLayer) +export(scale_size_animint) +export(selector.aes) +export(tests_exit) +export(tests_init) +export(tests_run) +export(theme_animint) +export(toRGB) +import(ggplot2) +import(plyr) diff --git a/R/animint.R b/R/animint.R new file mode 100644 index 000000000..5b3fc1e93 --- /dev/null +++ b/R/animint.R @@ -0,0 +1,1409 @@ +#' Convert a ggplot to a list. +#' @param meta environment with previously calculated plot data, and a new plot to parse, already stored in plot and plot.name. +#' @return nothing, info is stored in meta. +#' @export +#' @import ggplot2 plyr +parsePlot <- function(meta, plot, plot.name){ + ## adding data and mapping to each layer from base plot, if necessary + for(layer.i in seq_along(plot$layers)) { + + ## if data is not specified, get it from plot + if(length(plot$layers[[layer.i]]$data) == 0){ + plot$layers[[layer.i]]$data <- plot$data + } + + ## if mapping is not specified, get it from plot + if(is.null(plot$layers[[layer.i]]$mapping)){ + plot$layers[[layer.i]]$mapping <- plot$mapping + } + } + + built <- ggplot2::ggplot_build(plot) + plot.info <- list() + + ## Export axis specification as a combination of breaks and + ## labels, on the relevant axis scale (i.e. so that it can + ## be passed into d3 on the x axis scale instead of on the + ## grid 0-1 scale). This allows transformations to be used + ## out of the box, with no additional d3 coding. + theme.pars <- ggplot2:::plot_theme(plot) + + ## Interpret panel.margin as the number of lines between facets + ## (ignoring whatever grid::unit such as cm that was specified). + + ## Now ggplot specifies panel.margin in 'pt' instead of 'lines' + plot.info$panel_margin_lines <- pt.to.lines(theme.pars$panel.margin) + + ## No legend if theme(legend.postion="none"). + plot.info$legend <- if(theme.pars$legend.position != "none"){ + getLegendList(built) + } + + ## scan for legends in each layer. + for(layer.i in seq_along(plot$layers)){ + ##cat(sprintf("%4d / %4d layers\n", layer.i, length(plot$layers))) + ## This is the layer from the original ggplot object. + L <- plot$layers[[layer.i]] + ## If any legends are specified, add showSelected aesthetic + L <- addShowSelectedForLegend(meta, plot.info$legend, L) + }#layer.i + + ## need to call ggplot_build again because we've added to the plot. + ## I'm sure that there is a way around this, but not immediately sure how. + ## There's sort of a Catch-22 here because to create the interactivity, + ## we need to specify the variable corresponding to each legend. + ## To do this, we need to have the legend. + ## And to have the legend, I think that we need to use ggplot_build + built <- ggplot2::ggplot_build(plot) + ## TODO: implement a compiler that does not call ggplot_build at + ## all, and instead does all of the relevant computations in animint + ## code. + ## 'strips' are really titles for the different facet panels + plot.info$strips <- with(built, getStrips(plot$facet, panel)) + ## the layout tells us how to subset and where to plot on the JS side + plot.info$layout <- with(built, flag_axis(plot$facet, panel$layout)) + plot.info$layout <- with(built, train_layout( + plot$facet, plot$coordinates, plot.info$layout, panel$ranges)) + + # saving background info + plot.info$panel_background <- get_bg(theme.pars$panel.background, theme.pars) + plot.info$panel_border <- get_bg(theme.pars$panel.border, theme.pars) + + # extract major grid lines + plot.info$grid_major <- get_grid(theme.pars$panel.grid.major, theme.pars, + plot.info, meta, built) + # extract minor grid lines + plot.info$grid_minor <- get_grid(theme.pars$panel.grid.minor, theme.pars, + plot.info, meta, built, major = F) + + ## Flip labels if coords are flipped - transform does not take care + ## of this. Do this BEFORE checking if it is blank or not, so that + ## individual axes can be hidden appropriately, e.g. #1. + if("CoordFlip"%in%attr(plot$coordinates, "class")){ + temp <- plot$labels$x + plot$labels$x <- plot$labels$y + plot$labels$y <- temp + } + is.blank <- function(el.name){ + x <- ggplot2::calc_element(el.name, plot$theme) + "element_blank"%in%attr(x,"class") + } + + # Instead of an "axis" JSON object for each plot, + # allow for "axis1", "axis2", etc. where + # "axis1" corresponds to the 1st PANEL + ranges <- built$panel$ranges + n.axis <- length(ranges) + axes <- setNames(vector("list", n.axis), + paste0("axis", seq_len(n.axis))) + plot.info <- c(plot.info, axes) + + # translate axis information + for (xy in c("x", "y")) { + s <- function(tmp) sprintf(tmp, xy) + # one axis name per plot (ie, a xtitle/ytitle is shared across panels) + plot.info[[s("%stitle")]] <- if(is.blank(s("axis.title.%s"))){ + "" + } else { + scale.i <- which(plot$scales$find(xy)) + lab.or.null <- if(length(scale.i) == 1){ + plot$scales$scales[[scale.i]]$name + } + if(is.null(unlist(lab.or.null))){ + plot$labels[[xy]] + }else{ + lab.or.null + } + } + # theme settings are shared across panels + axis.text <- theme.pars[[s("axis.text.%s")]] + ## TODO: also look at axis.text! (and text?) + anchor <- hjust2anchor(axis.text$hjust) + angle <- if(is.numeric(axis.text$angle)){ + -axis.text$angle + } + if(is.null(angle)){ + angle <- 0 + } + if(is.null(anchor)){ + anchor <- if(angle == 0){ + "middle" + }else{ + "end" + } + } + plot.info[[s("%sanchor")]] <- as.character(anchor) + plot.info[[s("%sangle")]] <- as.numeric(angle) + # translate panel specific axis info + ctr <- 0 + for (axis in names(axes)) { + ctr <- ctr + 1 + range <- ranges[[ctr]] + plot.info[[axis]][[xy]] <- as.list(range[[s("%s.major_source")]]) + plot.info[[axis]][[s("%slab")]] <- if(is.blank(s("axis.text.%s"))){ + NULL + } else { + as.list(range[[s("%s.labels")]]) + } + plot.info[[axis]][[s("%srange")]] <- range[[s("%s.range")]] + plot.info[[axis]][[s("%sline")]] <- !is.blank(s("axis.line.%s")) + plot.info[[axis]][[s("%sticks")]] <- !is.blank(s("axis.ticks.%s")) + } + } + # grab the unique axis labels (makes rendering simpler) + plot.info <- getUniqueAxisLabels(plot.info) + + # grab plot title if present + plot.info$title <- getPlotTitle(theme.pars$plot.tiltle, + plot$labels$title) + + ## Set plot width and height from animint.* options if they are + ## present. + options_list <- getWidthAndHeight(plot$theme) + options_list <- setUpdateAxes(plot$theme, options_list) + plot.info$options <- options_list + + list( + plot.info=plot.info, + ggplot=plot, + built=built) +} + + +storeLayer <- function(meta, g, g.data.varied){ + ## Save each variable chunk to a separate tsv file. + meta$chunk.i <- 1L + meta$g <- g + g$chunks <- saveChunks(g.data.varied, meta) + g$total <- length(unlist(g$chunks)) + + ## Finally save to the master geom list. + meta$geoms[[g$classed]] <- g + g +} + +#' Save a layer to disk, save and return meta-data. +#' @param l one layer of the ggplot object. +#' @param d one layer of calculated data from ggplot2::ggplot_build(p). +#' @param meta environment of meta-data. +#' @param geom_num the number of geom in the plot. Each geom gets an increasing +#' ID number starting from 1 +#' @return list representing a layer, with corresponding aesthetics, ranges, and groups. +#' @export +saveLayer <- function(l, d, meta, layer_name, ggplot, built, AnimationInfo){ + # Set geom name and layer name + g <- list(geom=strsplit(layer_name, "_")[[1]][2]) + g$classed <- layer_name + + ranges <- built$panel$ranges + + ## needed for when group, etc. is an expression: + g$aes <- sapply(l$mapping, function(k) as.character(as.expression(k))) + + ## use un-named parameters so that they will not be exported + ## to JSON as a named object, since that causes problems with + ## e.g. colour. + ## 'colour', 'size' etc. have been moved to aes_params + g$params <- getLayerParams(l) + + ## Make a list of variables to use for subsetting. subset_order is the + ## order in which these variables will be accessed in the recursive + ## JavaScript array structure. + + ## subset_order IS in fact useful with geom_segment! For example, in + ## the first plot in the breakpointError example, the geom_segment has + ## the following exported data in plot.json + + ## "subset_order": [ + ## "showSelected", + ## "showSelected2" + ## ], + + ## This information is used to parse the recursive array data structure + ## that allows efficient lookup of subsets of data in JavaScript. Look at + ## the Firebug DOM browser on + ## http://sugiyama-www.cs.titech.ac.jp/~toby/animint/breakpoints/index.html + ## and navigate to plot.Geoms.geom3.data. You will see that this is a + ## recursive array that can be accessed via + ## data[segments][bases.per.probe] which is an un-named array + ## e.g. [{row1},{row2},...] which will be bound to the elements by + ## D3. The key point is that the subset_order array stores the order of the + ## indices that will be used to select the current subset of data (in + ## this case showSelected=segments, showSelected2=bases.per.probe). The + ## currently selected values of these variables are stored in + ## plot.Selectors. + + s.aes <- selector.aes(g$aes) + meta$selector.aes[[g$classed]] <- s.aes + + ## Do not copy group unless it is specified in aes, and do not copy + ## showSelected variables which are specified multiple times. + do.not.copy <- colsNotToCopy(g, s.aes) + copy.cols <- ! names(d) %in% do.not.copy + + g.data <- d[copy.cols] + + is.ss <- names(g$aes) %in% s.aes$showSelected$one + show.vars <- g$aes[is.ss] + pre.subset.order <- as.list(names(show.vars)) + + is.cs <- names(g$aes) %in% s.aes$clickSelects$one + update.vars <- g$aes[is.ss | is.cs] + + update.var.names <- if(0 < length(update.vars)){ + data.frame(variable=names(update.vars), value=NA) + } + + interactive.aes <- with(s.aes, { + rbind(clickSelects$several, showSelected$several, + update.var.names) + }) + + ## Construct the selector. + for(row.i in seq_along(interactive.aes$variable)){ + aes.row <- interactive.aes[row.i, ] + is.variable.value <- !is.na(aes.row$value) + selector.df <- if(is.variable.value){ + selector.vec <- g.data[[paste(aes.row$variable)]] + data.frame(value.col=aes.row$value, + selector.name=unique(paste(selector.vec))) + }else{ + value.col <- paste(aes.row$variable) + data.frame(value.col, + selector.name=update.vars[[value.col]]) + } + for(sel.i in 1:nrow(selector.df)){ + sel.row <- selector.df[sel.i,] + value.col <- paste(sel.row$value.col) + selector.name <- paste(sel.row$selector.name) + ## If this selector was defined by .variable .value aes, then we + ## will not generate selectize widgets. + meta$selectors[[selector.name]]$is.variable.value <- is.variable.value + ## If this selector has no defined type yet, we define it once + ## and for all here, so we can use it later for chunk + ## separation. + if(is.null(meta$selectors[[selector.name]]$type)){ + selector.type <- meta$selector.types[[selector.name]] + if(is.null(selector.type))selector.type <- "single" + stopifnot(is.character(selector.type)) + stopifnot(length(selector.type)==1) + stopifnot(selector.type %in% c("single", "multiple")) + meta$selectors[[selector.name]]$type <- selector.type + } + ## If this selector does not have any clickSelects then we show + ## the selectize widgets by default. + for(look.for in c("showSelected", "clickSelects")){ + if(grepl(look.for, aes.row$variable)){ + meta$selectors[[selector.name]][[look.for]] <- TRUE + } + } + ## We also store all the values of this selector in this layer, + ## so we can accurately set levels after all geoms have been + ## compiled. + value.vec <- unique(g.data[[value.col]]) + key <- paste(g$classed, row.i, sel.i) + meta$selector.values[[selector.name]][[key]] <- + list(values=paste(value.vec), update=g$classed) + } + } + + is.show <- grepl("showSelected", names(g$aes)) + has.show <- any(is.show) + ## Error if non-identity stat is used with showSelected, since + ## typically the stats will delete the showSelected column from the + ## built data set. For example geom_bar + stat_bin doesn't make + ## sense with clickSelects/showSelected, since two + ## clickSelects/showSelected values may show up in the same bin. + stat.type <- class(l$stat)[[1]] + checkForNonIdentityAndSS(stat.type, has.show, is.show, l, + g$classed, names(g.data), names(g$aes)) + + ## Warn if non-identity position is used with animint aes. + position.type <- class(l$position)[[1]] + if(has.show && position.type != "PositionIdentity"){ + print(l) + warning("showSelected only works with position=identity, problem: ", + g$classed) + } + + ##print("before pre-processing") + + ## Pre-process some complex geoms so that they are treated as + ## special cases of basic geoms. In ggplot2, this processing is done + ## in the draw method of the geoms. + if(g$geom=="abline"){ + ## loop through each set of slopes/intercepts + + ## TODO: vectorize this code! + for(i in 1:nrow(g.data)) { + + # "Trick" ggplot coord_transform into transforming the slope and intercept + g.data[i, "x"] <- ranges[[ g.data$PANEL[i] ]]$x.range[1] + g.data[i, "xend"] <- ranges[[ g.data$PANEL[i] ]]$x.range[2] + g.data[i, "y"] <- g.data$slope[i] * g.data$x[i] + g.data$intercept[i] + g.data[i, "yend"] <- g.data$slope[i] * g.data$xend[i] + g.data$intercept[i] + + # make sure that lines don't run off the graph + if(g.data$y[i] < ranges[[ g.data$PANEL[i] ]]$y.range[1] ) { + g.data$y[i] <- ranges[[ g.data$PANEL[i] ]]$y.range[1] + g.data$x[i] <- (g.data$y[i] - g.data$intercept[i]) / g.data$slope[i] + } + if(g.data$yend[i] > ranges[[ g.data$PANEL[i] ]]$y.range[2]) { + g.data$yend[i] <- ranges[[ g.data$PANEL[i] ]]$y.range[2] + g.data$xend[i] <- (g.data$yend[i] - g.data$intercept[i]) / g.data$slope[i] + } + } + ## ggplot2 defaults to adding a group aes for ablines! + ## Remove it since it is meaningless. + g$aes <- g$aes[names(g$aes)!="group"] + g.data <- g.data[! names(g.data) %in% c("slope", "intercept")] + g$geom <- "segment" + } else if(g$geom=="point"){ + # Fill set to match ggplot2 default of filled in circle. + # Check for fill in both data and params + fill.in.data <- ("fill" %in% names(g.data) && any(!is.na(g.data[["fill"]]))) + fill.in.params <- "fill" %in% names(g$params) + fill.specified <- fill.in.data || fill.in.params + if(!fill.specified & "colour" %in% names(g.data)){ + g.data[["fill"]] <- g.data[["colour"]] + } + } else if(g$geom=="text"){ + ## convert hjust to anchor. + hjustRemove <- function(df.or.list){ + df.or.list$anchor <- hjust2anchor(df.or.list$hjust) + df.or.list[names(df.or.list) != "hjust"] + } + vjustWarning <- function(vjust.vec){ + not.supported <- vjust.vec != 0 + if(any(not.supported)){ + bad.vjust <- unique(vjust.vec[not.supported]) + print(bad.vjust) + warning("animint only supports vjust=0") + } + } + if ("hjust" %in% names(g$params)) { + g$params <- hjustRemove(g$params) + } else if ("hjust" %in% names(g.data)) { + g.data <- hjustRemove(g.data) + } + if("vjust" %in% names(g$params)) { + vjustWarning(g$params$vjust) + } else if ("vjust" %in% names(g$aes)) { + vjustWarning(g.data$vjust) + } + } else if(g$geom=="ribbon"){ + # Color set to match ggplot2 default of fill with no outside border. + if("fill"%in%names(g.data) & !"colour"%in%names(g.data)){ + g.data[["colour"]] <- g.data[["fill"]] + } + } else if(g$geom=="density" | g$geom=="area"){ + g$geom <- "ribbon" + } else if(g$geom=="tile" | g$geom=="raster" | g$geom=="histogram" ){ + # Color set to match ggplot2 default of tile with no outside border. + if(!"colour"%in%names(g.data) & "fill"%in%names(g.data)){ + g.data[["colour"]] <- g.data[["fill"]] + # Make outer border of 0 size if size isn't already specified. + if(!"size"%in%names(g.data)) g.data[["size"]] <- 0 + } + g$geom <- "rect" + } else if(g$geom=="bar"){ + is.xy <- names(g.data) %in% c("x", "y") + g.data <- g.data[!is.xy] + g$geom <- "rect" + } else if(g$geom=="bin2d"){ + stop("bin2d is not supported in animint. Try using geom_tile() and binning the data yourself.") + } else if(g$geom=="boxplot"){ + stop("boxplots are not supported. Workaround: rects, lines, and points") + ## TODO: boxplot support. But it is hard since boxplots are drawn + ## using multiple geoms and it is not straightforward to deal with + ## that using our current JS code. There is a straightforward + ## workaround: combine working geoms (rects, lines, and points). + + g.data$outliers <- sapply(g.data$outliers, FUN=paste, collapse=" @ ") + # outliers are specified as a list... change so that they are specified + # as a single string which can then be parsed in JavaScript. + # there has got to be a better way to do this!! + } else if(g$geom=="violin"){ + g.data$xminv <- with(g.data, x - violinwidth * (x - xmin)) + g.data$xmaxv <- with(g.data, x + violinwidth * (xmax - x)) + newdata <- plyr::ddply(g.data, "group", function(df){ + rbind(plyr::arrange(transform(df, x=xminv), y), + plyr::arrange(transform(df, x=xmaxv), -y)) + }) + newdata <- plyr::ddply(newdata, "group", function(df) rbind(df, df[1,])) + g.data <- newdata + g$geom <- "polygon" + } else if(g$geom=="step"){ + datanames <- names(g.data) + g.data <- plyr::ddply(g.data, "group", function(df) ggplot2:::stairstep(df)) + g$geom <- "path" + } else if(g$geom=="contour" | g$geom=="density2d"){ + g$aes[["group"]] <- "piece" + g$geom <- "path" + } else if(g$geom=="freqpoly"){ + g$geom <- "line" + } else if(g$geom=="quantile"){ + g$geom <- "path" + } else if(g$geom=="hex"){ + g$geom <- "polygon" + ## TODO: for interactivity we will run into the same problems as + ## we did with histograms. Again, if we put several + ## clickSelects/showSelected values in the same hexbin, then + ## clicking/hiding hexbins doesn't really make sense. Need to stop + ## with an error if showSelected/clickSelects is used with hex. + g$aes[["group"]] <- "group" + dx <- ggplot2::resolution(g.data$x, FALSE) + dy <- ggplot2::resolution(g.data$y, FALSE) / sqrt(3) / 2 * 1.15 + hex <- as.data.frame(hexbin::hexcoords(dx, dy))[,1:2] + hex <- rbind(hex, hex[1,]) # to join hexagon back to first point + g.data$group <- as.numeric(interaction(g.data$group, 1:nrow(g.data))) + ## this has the potential to be a bad assumption - + ## by default, group is identically 1, if the user + ## specifies group, polygons aren't possible to plot + ## using d3, because group will have a different meaning + ## than "one single polygon". + # CPS (07-24-14) what about this? -- + # http://tdhock.github.io/animint/geoms/polygon/index.html + newdata <- plyr::ddply(g.data, "group", function(df){ + df$xcenter <- df$x + df$ycenter <- df$y + cbind(x=df$x+hex$x, y=df$y+hex$y, df[,-which(names(df)%in%c("x", "y"))]) + }) + g.data <- newdata + # Color set to match ggplot2 default of tile with no outside border. + if(!"colour"%in%names(g.data) & "fill"%in%names(g.data)){ + g.data[["colour"]] <- g.data[["fill"]] + # Make outer border of 0 size if size isn't already specified. + if(!"size"%in%names(g.data)) g.data[["size"]] <- 0 + } + } + + ## Some geoms need their data sorted before saving to tsv. + if(g$geom %in% c("ribbon", "line")){ + g.data <- g.data[order(g.data$x), ] + } + + ## Check g.data for color/fill - convert to hexadecimal so JS can parse correctly. + for(color.var in c("colour", "color", "fill")){ + if(color.var %in% names(g.data)){ + g.data[,color.var] <- toRGB(g.data[,color.var]) + } + if(color.var %in% names(g$params)){ + g$params[[color.var]] <- toRGB(g$params[[color.var]]) + } + } + + has.no.fill <- g$geom %in% c("path", "line") + zero.size <- any(g.data$size == 0, na.rm=TRUE) + if(zero.size && has.no.fill){ + warning(sprintf("geom_%s with size=0 will be invisible",g$geom)) + } + ## TODO: coord_transform maybe won't work for + ## geom_dotplot|rect|segment and polar/log transformations, which + ## could result in something nonlinear. For the time being it is + ## best to just ignore this, but you can look at the source of + ## e.g. geom-rect.r in ggplot2 to see how they deal with this by + ## doing a piecewise linear interpolation of the shape. + + ## Flip axes in case of coord_flip + if(inherits(ggplot$coordinates, "CoordFlip")){ + names(g.data) <- switch_axes(names(g.data)) + } + + ## Output types + ## Check to see if character type is d3's rgb type. + g$types <- sapply(g.data, function(x) { + type <- paste(class(x), collapse="-") + if(type == "character"){ + if(sum(!is.rgb(x))==0){ + "rgb" + }else if(sum(!is.linetype(x))==0){ + "linetype" + }else { + "character" + } + }else{ + type + } + }) + g$types[["group"]] <- "character" + + ## convert ordered factors to unordered factors so javascript + ## doesn't flip out. + ordfactidx <- which(g$types=="ordered-factor") + for(i in ordfactidx){ + g.data[[i]] <- factor(as.character(g.data[[i]])) + g$types[[i]] <- "factor" + } + + ## Get unique values of time variable. + time.col <- NULL + if(is.list(AnimationInfo$time)){ # if this is an animation, + g.time.list <- list() + for(c.or.s in names(s.aes)){ + cs.info <- s.aes[[c.or.s]] + for(a in cs.info$one){ + if(g$aes[[a]] == AnimationInfo$time$var){ + g.time.list[[a]] <- g.data[[a]] + time.col <- a + } + } + for(row.i in seq_along(cs.info$several$value)){ + cs.row <- cs.info$several[row.i,] + c.name <- paste(cs.row$variable) + is.time <- g.data[[c.name]] == AnimationInfo$time$var + g.time.list[[c.name]] <- g.data[is.time, paste(cs.row$value)] + } + } + u.vals <- unique(unlist(g.time.list)) + if(length(u.vals)){ + AnimationInfo$timeValues[[paste(g$classed)]] <- sort(u.vals) + } + } + ## Make the time variable the first subset_order variable. + if(length(time.col)){ + pre.subset.order <- pre.subset.order[order(pre.subset.order != time.col)] + } + + ## Determine which showSelected values to use for breaking the data + ## into chunks. This is a list of variables which have the same + ## names as the selectors. E.g. if chunk_order=list("year") then + ## when year is clicked, we may need to download some new data for + ## this geom. + + subset.vec <- unlist(pre.subset.order) + if("chunk_vars" %in% names(g$params)){ #designer-specified chunk vars. + designer.chunks <- g$params$chunk_vars + if(!is.character(designer.chunks)){ + stop("chunk_vars must be a character vector; ", + "use chunk_vars=character() to specify 1 chunk") + } + not.subset <- !designer.chunks %in% g$aes[subset.vec] + if(any(not.subset)){ + stop("invalid chunk_vars ", + paste(designer.chunks[not.subset], collapse=" "), + "; possible showSelected variables: ", + paste(g$aes[subset.vec], collapse=" ")) + } + is.chunk <- g$aes[subset.vec] %in% designer.chunks + chunk.cols <- subset.vec[is.chunk] + nest.cols <- subset.vec[!is.chunk] + }else{ #infer a default, either 0 or 1 chunk vars: + if(length(meta$selectors)==0){ + ## no selectors, just make 1 chunk. + nest.cols <- subset.vec + chunk.cols <- NULL + }else{ + selector.types <- sapply(meta$selectors, "[[", "type") + selector.names <- g$aes[subset.vec] + subset.types <- selector.types[selector.names] + can.chunk <- subset.types != "multiple" + names(can.chunk) <- subset.vec + ## Guess how big the chunk files will be, and reduce the number of + ## chunks if there are any that are too small. + tmp <- tempfile() + some.lines <- rbind(head(g.data), tail(g.data)) + write.table(some.lines, tmp, + col.names=FALSE, + quote=FALSE, row.names=FALSE, sep="\t") + bytes <- file.info(tmp)$size + bytes.per.line <- bytes/nrow(some.lines) + bad.chunk <- function(){ + if(all(!can.chunk))return(NULL) + can.chunk.cols <- subset.vec[can.chunk] + maybe.factors <- g.data[, can.chunk.cols, drop=FALSE] + for(N in names(maybe.factors)){ + maybe.factors[[N]] <- paste(maybe.factors[[N]]) + } + rows.per.chunk <- table(maybe.factors) + bytes.per.chunk <- rows.per.chunk * bytes.per.line + if(all(4096 < bytes.per.chunk))return(NULL) + ## If all of the tsv chunk files are greater than 4KB, then we + ## return NULL here to indicate that the current chunk + ## variables (indicated in can.chunk) are fine. + + ## In other words, the compiler will not break a geom into + ## chunks if any of the resulting chunk tsv files is estimated + ## to be less than 4KB (of course, if the layer has very few + ## data overall, the compiler creates 1 file which may be less + ## than 4KB, but that is fine). + dim.byte.list <- list() + if(length(can.chunk.cols) == 1){ + dim.byte.list[[can.chunk.cols]] <- sum(bytes.per.chunk) + }else{ + for(dim.i in seq_along(can.chunk.cols)){ + dim.name <- can.chunk.cols[[dim.i]] + dim.byte.list[[dim.name]] <- + apply(bytes.per.chunk, -dim.i, sum) + } + } + selector.df <- + data.frame(chunks.for=length(rows.per.chunk), + chunks.without=sapply(dim.byte.list, length), + min.bytes=sapply(dim.byte.list, min)) + ## chunks.for is the number of chunks you get if you split the + ## data set using just this column. If it is 1, then it is + ## fine to chunk on this variable (since we certainly won't + ## make more than 1 small tsv file) and in fact we want to + ## chunk on this variable, since then this layer's data won't + ## be downloaded at first if it is not needed. + not.one <- subset(selector.df, 1 < chunks.for) + if(nrow(not.one) == 0){ + NULL + }else{ + rownames(not.one)[[which.max(not.one$min.bytes)]] + } + } + while({ + bad <- bad.chunk() + !is.null(bad) + }){ + can.chunk[[bad]] <- FALSE + } + if(any(can.chunk)){ + nest.cols <- subset.vec[!can.chunk] + chunk.cols <- subset.vec[can.chunk] + }else{ + nest.cols <- subset.vec + chunk.cols <- NULL + } + } # meta$selectors > 0 + } + + # If there is only one PANEL, we don't need it anymore. + # g$PANEL <- unique(g.data[["PANEL"]]) + plot.has.panels <- nrow(built$panel$layout) > 1 + g.data <- removeUniquePanelValue(g.data, plot.has.panels) + + ## Also add pointers to these chunks from the related selectors. + if(length(chunk.cols)){ + selector.names <- as.character(g$aes[chunk.cols]) + chunk.name <- paste(selector.names, collapse="_") + g$chunk_order <- as.list(selector.names) + for(selector.name in selector.names){ + meta$selectors[[selector.name]]$chunks <- + unique(c(meta$selectors[[selector.name]]$chunks, chunk.name)) + } + }else{ + g$chunk_order <- list() + } + g$nest_order <- as.list(nest.cols) + names(g$chunk_order) <- NULL + names(g$nest_order) <- NULL + g$subset_order <- g$nest_order + + ## If this plot has more than one PANEL then add it to subset_order + ## and nest_order. + if(plot.has.panels){ + g$subset_order <- c(g$subset_order, "PANEL") + g$nest_order <- c(g$nest_order, "PANEL") + } + + ## nest_order should contain both .variable .value aesthetics, but + ## subset_order should contain only .variable. + if(0 < nrow(s.aes$showSelected$several)){ + g$nest_order <- with(s.aes$showSelected$several, { + c(g$nest_order, paste(variable), paste(value)) + }) + g$subset_order <- + c(g$subset_order, paste(s.aes$showSelected$several$variable)) + } + + ## group should be the last thing in nest_order, if it is present. + data.object.geoms <- c("line", "path", "ribbon", "polygon") + if("group" %in% names(g$aes) && g$geom %in% data.object.geoms){ + g$nest_order <- c(g$nest_order, "group") + } + + ## Some geoms should be split into separate groups if there are NAs. + if(any(is.na(g.data)) && "group" %in% names(g$aes)){ + sp.cols <- unlist(c(chunk.cols, g$nest_order)) + order.args <- list() + for(sp.col in sp.cols){ + order.args[[sp.col]] <- g.data[[sp.col]] + } + ord <- do.call(order, order.args) + g.data <- g.data[ord,] + is.missing <- apply(is.na(g.data), 1, any) + diff.vec <- diff(is.missing) + new.group.vec <- c(FALSE, diff.vec == 1) + for(chunk.col in sp.cols){ + one.col <- g.data[[chunk.col]] + is.diff <- c(FALSE, one.col[-1] != one.col[-length(one.col)]) + new.group.vec[is.diff] <- TRUE + } + subgroup.vec <- cumsum(new.group.vec) + g.data$group <- subgroup.vec + } + + ## Determine if there are any "common" data that can be saved + ## separately to reduce disk usage. + data.or.null <- getCommonChunk(g.data, chunk.cols, g$aes) + g.data.varied <- if(is.null(data.or.null)){ + split.x(na.omit(g.data), chunk.cols) + }else{ + g$columns$common <- as.list(names(data.or.null$common)) + tsv.name <- sprintf("%s_chunk_common.tsv", g$classed) + tsv.path <- file.path(meta$out.dir, tsv.name) + write.table(data.or.null$common, tsv.path, + quote = FALSE, row.names = FALSE, + sep = "\t") + data.or.null$varied + } + + list(g=g, g.data.varied=g.data.varied, timeValues=AnimationInfo$timeValues) +} + + +##' Parse selectors from aes names. +##' @title Parse selectors from aes names. +##' @param a.vec character vector of aes names. +##' @return list of selector info. +##' @author Toby Dylan Hocking +##' @export +selector.aes <- function(a.list){ + a.vec <- names(a.list) + if(is.null(a.vec))a.vec <- character() + stopifnot(is.character(a.vec)) + cs.or.ss <- grepl("clickSelects|showSelected", a.vec) + for(v in c("value", "variable")){ + regex <- paste0("[.]", v, "$") + is.v <- grepl(regex, a.vec) + if(any(is.v)){ + a <- a.vec[is.v & cs.or.ss] + other.v <- if(v=="value")"variable" else "value" + other.a <- sub(paste0(v, "$"), other.v, a) + not.found <- ! other.a %in% a.vec + if(any(not.found)){ + stop(".variable or .value aes not found") + } + } + } + aes.list <- list() + for(a in c("clickSelects", "showSelected")){ + is.a <- grepl(a, a.vec) + is.value <- grepl("[.]value$", a.vec) + is.variable <- grepl("[.]variable$", a.vec) + var.or.val <- is.variable | is.value + a.value <- a.vec[is.a & is.value] + a.variable <- sub("value$", "variable", a.value) + single <- a.vec[is.a & (!var.or.val)] + ignored <- c() + if(1 < length(single)){ + single.df <- data.frame( + aes.name=single, + data.var=paste(a.list[single])) + single.sorted <- single.df[order(single.df$data.var), ] + single.sorted$keep <- c(TRUE, diff(as.integer(single.df$data.var))!=0) + single <- with(single.sorted, paste(aes.name[keep])) + ignored <- with(single.sorted, paste(aes.name[!keep])) + } + aes.list[[a]] <- + list(several=data.frame(variable=a.variable, value=a.value), + one=single, ignored=ignored) + } + aes.list +} + + +#' Compile and render an animint in a local directory +#' +#' An animint is a list of ggplots and options that defines +#' an interactive animation and can be viewed in a web browser. +#' Several new aesthetics control interactivity. +#' The most important two are +#' \itemize{ +#' \item \code{aes(showSelected=variable)} means that +#' only the subset of the data that corresponds to +#' the selected value of variable will be shown. +#' \item \code{aes(clickSelects=variable)} means that clicking +#' this geom will change the currently selected value of variable. +#' } +#' The others are described on https://github.com/tdhock/animint/wiki/Advanced-features-present-animint-but-not-in-ggplot2 +#' +#' Supported ggplot2 geoms: +#' \itemize{ +#' \item point +#' \item jitter +#' \item line +#' \item rect +#' \item tallrect (new with this package) +#' \item segment +#' \item hline +#' \item vline +#' \item bar +#' \item text +#' \item tile +#' \item raster +#' \item ribbon +#' \item abline +#' \item density +#' \item path +#' \item polygon +#' \item histogram +#' \item violin +#' \item linerange +#' \item step +#' \item contour +#' \item density2d +#' \item area +#' \item freqpoly +#' \item hex +#' } +#' Unsupported geoms: +#' \itemize{ +#' \item rug +#' \item dotplot +#' \item quantile - should *theoretically* work but in practice does not work +#' \item smooth - can be created using geom_line and geom_ribbon +#' \item boxplot - can be created using geom_rect and geom_segment +#' \item crossbar - can be created using geom_rect and geom_segment +#' \item pointrange - can be created using geom_linerange and geom_point +#' \item bin2d - bin using ddply() and then use geom_tile() +#' \item map - can be created using geom_polygon or geom_path +#'} +#' Supported scales: +#' \itemize{ +#' \item alpha, +#' \item fill/colour (brewer, gradient, identity, manual) +#' \item linetype +#' \item x and y axis scales, manual break specification, label formatting +#' \item x and y axis theme elements: axis.line, axis.ticks, axis.text, axis.title can be set to element_blank(); other theme modifications not supported at this time, but would be possible with custom css files. +#' \item area +#' \item size +#' } +#' Unsupported scales: +#' \itemize{ +#' \item shape. Open and closed circles can be represented by manipulating fill and colour scales and using default (circle) points, but d3 does not support many R shape types, so mapping between the two is difficult. +#' } +#' +#' @aliases animint +#' @param plot.list a named list of ggplots and option lists. +#' @param out.dir directory to store html/js/csv files. +#' @param json.file character string that names the JSON file with metadata associated with the plot. +#' @param open.browser Should R open a browser? If yes, be sure to configure your browser to allow access to local files, as some browsers block this by default (e.g. chrome). +#' @param css.file character string for non-empty css file to include. Provided file will be copied to the output directory as styles.css +#' @return invisible list of ggplots in list format. +#' @export +#' @seealso \code{\link{ggplot2}} +#' @example inst/examples/animint.R +animint2dir <- function(plot.list, out.dir = tempfile(), + json.file = "plot.json", open.browser = interactive(), + css.file = "") { + ## Check plot.list for errors + checkPlotList(plot.list) + + ## Store meta-data in this environment, so we can alter state in the + ## lower-level functions. + meta <- newEnvironment() + meta$selector.types <- plot.list$selector.types + dir.create(out.dir,showWarnings=FALSE) + meta$out.dir <- out.dir + + ## Store the animation information (time, var, ms) in a separate list + AnimationInfo <- list() + + ## Save the animation variable so we can treat it specially when we + ## process each geom. + # CPS (7-22-14): What if the user doesn't specify milliseconds? Could we provide a reasonable default? + if(is.list(plot.list[["time"]])){ + # Check animation variable for errors + checkAnimationTimeVar(plot.list$time) + AnimationInfo$time <- plot.list$time + ms <- AnimationInfo$time$ms + time.var <- AnimationInfo$time$variable + } + + ## The title option should just be a character, not a list. + if(is.list(plot.list$title)){ + plot.list$title <- plot.list$title[[1]] + } + if(is.character(plot.list$title)){ + meta$title <- plot.list$title[[1]] + plot.list$title <- NULL + } + + ## Extract essential info from ggplots, reality checks. + ggplot.list <- list() + AllPlotsInfo <- list() + for(list.name in names(plot.list)){ + p <- plot.list[[list.name]] + if(is.ggplot(p)){ + ## Before calling ggplot_build, we do some error checking for + ## some animint extensions. + checkPlotForAnimintExtensions(p, list.name) + + ## If plot is correct, save to meta for further processing + parsed_info <- parsePlot(meta, p, list.name) # calls ggplot_build. + AllPlotsInfo[[list.name]] <- parsed_info[[1]] + ggplot.list[[list.name]] <- parsed_info[2:3] + }else if(is.list(p)){ ## for options. + meta[[list.name]] <- p + }else{ + stop("list items must be ggplots or option lists, problem: ", list.name) + } + } + + ## After going through all of the meta-data in all of the ggplots, + ## now we have enough info to save the TSV file database. + geom_num <- 0 + g.list <- list() + for(p.name in names(ggplot.list)){ + ggplot.info <- ggplot.list[[p.name]] + for(layer.i in seq_along(ggplot.info$ggplot$layers)){ + L <- ggplot.info$ggplot$layers[[layer.i]] + df <- ggplot.info$built$data[[layer.i]] + ## cat(sprintf( + ## "saving layer %4d / %4d of ggplot %s\n", + ## layer.i, length(ggplot.info$built$data), + ## p.name)) + + ## Data now contains columns with fill, alpha, colour etc. + ## Remove from data if they have a single unique value and + ## are NOT used in mapping to reduce tsv file size + redundant.cols <- names(L$geom$default_aes) + for(col.name in names(df)){ + if(col.name %in% redundant.cols){ + all.vals <- unique(df[[col.name]]) + if(length(all.vals) == 1){ + in.mapping <- + !is.null(L$mapping[[col.name]]) + if(!in.mapping){ + df[[col.name]] <- NULL + } + } + } + } + geom_num <- geom_num + 1 + layer_name <- getLayerName(L, geom_num, p.name) + gl <- saveLayer(L, df, meta, layer_name, + ggplot.info$ggplot, ggplot.info$built, AnimationInfo) + + ## Save Animation Info separately + AnimationInfo$timeValues <- gl$timeValues + gl$timeValues <- NULL + ## Save to a list before saving to tsv + ## Helps during axis updates and Inf values + g.list[[p.name]][[gl$g$classed]] <- gl + }#layer.i + } + + ## Selector levels and update were stored in saveLayer, so now + ## compute the unique values to store in meta$selectors. + for(selector.name in names(meta$selector.values)){ + values.update <- meta$selector.values[[selector.name]] + value.vec <- unique(unlist(lapply(values.update, "[[", "values"))) + meta$selectors[[selector.name]]$selected <- if( + meta$selectors[[selector.name]]$type=="single"){ + value.vec[1] + }else{ + value.vec + } + ## Check the selectize option to determine if the designer wants + ## to show a widget for this selector. + selectize <- meta$selectize[[selector.name]] + render.widget <- if(is.logical(selectize)){ + selectize[1] + }else{ + ## If the designer did not set selectize, then we set a default + ## (if .variable .value aes, then no selectize; otherwise if + ## there are less than 1000 values then yes). + if(isTRUE(meta$selectors[[selector.name]]$is.variable.value)){ + FALSE + }else{ + if(length(value.vec) < 1000){ + TRUE + }else{ + FALSE + } + } + } + if(render.widget){ + ## Showing selectize widgets is optional, and indicated to the + ## renderer by the compiler by not setting the "levels" + ## attribute of the selector. + meta$selectors[[selector.name]]$levels <- value.vec + } + ## s.info$update is the list of geom names that will be updated + ## for this selector. + meta$selectors[[selector.name]]$update <- + as.list(unique(unlist(lapply(values.update, "[[", "update")))) + } + + ## For a static data viz with no interactive aes, no need to check + ## for trivial showSelected variables with only 1 level. + checkSingleShowSelectedValue(meta$selectors) + + ## Go through options and add to the list. + for(v.name in names(meta$duration)){ + meta$selectors[[v.name]]$duration <- meta$duration[[v.name]] + } + ## Set plot sizes. + setPlotSizes(meta, AllPlotsInfo) + + ## Get domains of data subsets if theme_animint(update_axes) is used + for(p.name in names(ggplot.list)){ + axes_to_update <- AllPlotsInfo[[p.name]]$options$update_axes + if(!is.null(axes_to_update)){ + for (axis in axes_to_update){ + subset_domains <- list() + # Determine if every panel needs a different domain or not + # We conclude here if we want to split the data by PANEL + # for the axes updates. Else every panel uses the same domain + panels <- ggplot.list[[p.name]]$built$panel$layout$PANEL + axes_drawn <- + ggplot.list[[p.name]]$built$panel$layout[[paste0("AXIS_", + toupper(axis))]] + panels_used <- panels[axes_drawn] + split_by_panel <- all(panels == panels_used) + for(num in seq_along(ggplot.list[[p.name]]$built$plot$layers)){ + # If there is a geom where the axes updates have non numeric values, + # we stop and throw an informative warning + # It does not make sense to have axes updates for non numeric values + aesthetic_names <- names(g.list[[p.name]][[num]]$g$aes) + + axis_col_name <- aesthetic_names[grepl(axis, aesthetic_names)] + axis_col <- g.list[[p.name]][[num]]$g$aes[[ axis_col_name[[1]] ]] + axis_is_numeric <- is.numeric(ggplot.list[[p.name]]$built$plot$layers[[num]]$data[[axis_col]]) + if(!axis_is_numeric){ + stop(paste0("'update_axes' specified for '", toupper(axis), + "' axis on plot '", p.name, + "' but the column '", axis_col, "' is non-numeric.", + " Axes updates are only available for numeric data.")) + } + + # handle cases for showSelected: showSelectedlegendfill, + # showSelectedlegendcolour etc. + choose_ss <- grepl("^showSelected", aesthetic_names) + ss_selectors <- g.list[[p.name]][[num]]$g$aes[choose_ss] + # Do not calculate domains for multiple selectors + remove_ss <- c() + for(j in seq_along(ss_selectors)){ + if(meta$selectors[[ ss_selectors[[j]] ]]$type != "single"){ + remove_ss <- c(remove_ss, ss_selectors[j]) + } + } + ss_selectors <- ss_selectors[!ss_selectors %in% remove_ss] + # Only save those selectors which are used by plot + for(ss in ss_selectors){ + if(!ss %in% AllPlotsInfo[[p.name]]$axis_domains[[axis]]$selectors){ + AllPlotsInfo[[p.name]]$axis_domains[[axis]]$selectors <- + c(ss, AllPlotsInfo[[p.name]]$axis_domains[[axis]]$selectors) + } + } + if(length(ss_selectors) > 0){ + subset_domains[num] <- compute_domains( + ggplot.list[[p.name]]$built$data[[num]], + axis, strsplit(names(g.list[[p.name]])[[num]], "_")[[1]][[2]], + names(sort(ss_selectors)), split_by_panel) + } + } + subset_domains <- subset_domains[!sapply(subset_domains, is.null)] + if(length(subset_domains) > 0){ + use_domain <- get_domain(subset_domains) + # Save for renderer + AllPlotsInfo[[p.name]]$axis_domains[[axis]]$domains <- use_domain + # Get gridlines for updates + AllPlotsInfo[[p.name]]$axis_domains[[axis]]$grids <- + get_ticks_gridlines(use_domain) + ## Initially selected selector values are stored in curr_select + ## which updates every time a user updates the axes + saved_selectors <- sort(names(meta$selectors)) + for (ss in saved_selectors){ + if(ss %in% AllPlotsInfo[[p.name]]$axis_domains[[axis]]$selectors){ + AllPlotsInfo[[p.name]]$axis_domains[[axis]]$curr_select[[ss]] <- + meta$selectors[[ss]]$selected + } + } + }else{ + warning(paste("update_axes specified for", toupper(axis), + "axis on plot", p.name, + "but found no geoms with showSelected=singleSelectionVariable,", + "so created a plot with no updates for", + toupper(axis), "axis"), call. = FALSE) + # Do not save in plot.json file if axes is not getting updated + update_axes <- AllPlotsInfo[[p.name]]$options$update_axes + AllPlotsInfo[[p.name]]$options$update_axes <- + update_axes[!axis == update_axes] + } + } + } + } + + ## Finally save all the layers + for(p.name in names(ggplot.list)){ + for(g1 in seq_along(g.list[[p.name]])){ + g <- storeLayer(meta, g.list[[p.name]][[g1]]$g, + g.list[[p.name]][[g1]]$g.data.varied) + ## Every plot has a list of geom names. + AllPlotsInfo[[p.name]]$geoms <- c( + AllPlotsInfo[[p.name]]$geoms, list(g$classed)) + }#layer.i + } + + ## Now that selectors are all defined, go back through geoms to + ## check if there are any warnings to issue. + issueSelectorWarnings(meta$geoms, meta$selector.aes, meta$duration) + + ## These geoms need to be updated when the time.var is animated, so + ## let's make a list of all possible values to cycle through, from + ## all the values used in those geoms. + if("time" %in% names(AnimationInfo)){ + meta$selectors[[AnimationInfo$time$variable]]$type <- "single" + anim.values <- AnimationInfo$timeValues + if(length(AnimationInfo$timeValues)==0){ + stop("no interactive aes for time variable ", AnimationInfo$time$variable) + } + anim.not.null <- anim.values[!sapply(anim.values, is.null)] + time.classes <- sapply(anim.not.null, function(x) class(x)[1]) + time.class <- time.classes[[1]] + if(any(time.class != time.classes)){ + print(time.classes) + stop("time variables must all have the same class") + } + AnimationInfo$time$sequence <- if(time.class=="POSIXct"){ + orderTime <- function(format){ + values <- unlist(sapply(anim.not.null, strftime, format)) + sort(unique(as.character(values))) + } + hms <- orderTime("%H:%M:%S") + f <- if(length(hms) == 1){ + "%Y-%m-%d" + }else{ + "%Y-%m-%d %H:%M:%S" + } + orderTime(f) + }else if(time.class=="factor"){ + levs <- levels(anim.not.null[[1]]) + if(any(sapply(anim.not.null, function(f)levels(f)!=levs))){ + print(sapply(anim.not.null, levels)) + stop("all time factors must have same levels") + } + levs + }else{ #character, numeric, integer, ... what else? + as.character(sort(unique(unlist(anim.not.null)))) + } + meta$selectors[[time.var]]$selected <- AnimationInfo$time$sequence[[1]] + } + + ## The first selection: + for(selector.name in names(meta$first)){ + first <- as.character(meta$first[[selector.name]]) + if(selector.name %in% names(meta$selectors)){ + s.type <- meta$selectors[[selector.name]]$type + if(s.type == "single"){ + stopifnot(length(first) == 1) + } + meta$selectors[[selector.name]]$selected <- first + }else{ + print(list(selectors=names(meta$selectors), + missing.first=selector.name)) + stop("missing first selector variable") + } + } + + meta$plots <- AllPlotsInfo + meta$time <- AnimationInfo$time + meta$timeValues <- AnimationInfo$timeValues + ## Finally, copy html/js/json files to out.dir. + src.dir <- system.file("htmljs",package="animint2") + to.copy <- Sys.glob(file.path(src.dir, "*")) + if(file.exists(paste0(out.dir, "styles.css")) | css.file != "default.file"){ + to.copy <- to.copy[!grepl("styles.css", to.copy, fixed=TRUE)] + } + if(css.file!=""){ + # if css filename is provided, copy that file to the out directory as "styles.css" + to.copy <- to.copy[!grepl("styles.css", to.copy, fixed=TRUE)] + if(!file.exists(css.file)){ + stop(paste("css.file", css.file, "does not exist. Please check that the file name and path are specified correctly.")) + } else { + file.copy(css.file, file.path(out.dir, "styles.css"), overwrite=TRUE) + } + } else { + style.file <- system.file("htmljs", "styles.css", package = "animint2") + file.copy(style.file, file.path(out.dir, "styles.css"), overwrite=TRUE) + } + file.copy(to.copy, out.dir, overwrite=TRUE, recursive=TRUE) + export.names <- + c("geoms", "time", "duration", "selectors", "plots", "title") + export.data <- list() + for(export.name in export.names){ + if(export.name %in% ls(meta)){ + export.data[[export.name]] <- meta[[export.name]] + } + } + json <- RJSONIO::toJSON(export.data) + cat(json, file = file.path(out.dir, json.file)) + if (open.browser) { + message('opening a web browser with a file:// URL; ', + 'if the web page is blank, try running +if (!requireNamespace("servr")) install.packages("servr") +servr::httd("', normalizePath( out.dir,winslash="/" ), '")') + browseURL(sprintf("%s/index.html", out.dir)) + } + invisible(meta) + ### An invisible copy of the R list that was exported to JSON. +} + + +#' Function to get legend information from ggplot +#' @param plistextra output from ggplot2::ggplot_build(p) +#' @return list containing information for each legend +#' @export +getLegendList <- function(plistextra){ + plot <- plistextra$plot + scales <- plot$scales + layers <- plot$layers + default_mapping <- plot$mapping + theme <- ggplot2:::plot_theme(plot) + position <- theme$legend.position + # by default, guide boxes are vertically aligned + if(is.null(theme$legend.box)) theme$legend.box <- "vertical" else theme$legend.box + + # size of key (also used for bar in colorbar guide) + if(is.null(theme$legend.key.width)) theme$legend.key.width <- theme$legend.key.size + if(is.null(theme$legend.key.height)) theme$legend.key.height <- theme$legend.key.size + # by default, direction of each guide depends on the position of the guide. + if(is.null(theme$legend.direction)){ + theme$legend.direction <- + if (length(position) == 1 && position %in% c("top", "bottom", "left", "right")) + switch(position[1], top =, bottom = "horizontal", left =, right = "vertical") + else + "vertical" + } + # justification of legend boxes + theme$legend.box.just <- + if(is.null(theme$legend.box.just)) { + if (length(position) == 1 && position %in% c("top", "bottom", "left", "right")) + switch(position, bottom =, top = c("center", "top"), left =, right = c("left", "top")) + else + c("center", "center") + } + + position <- theme$legend.position + # locate guide argument in scale_*, and use that for a default. + # Note, however, that guides(colour = ...) has precendence! See https://gist.github.com/cpsievert/ece28830a6c992b29ab6 + guides.args <- list() + for(aes.name in c("colour", "fill")){ + aes.loc <- which(scales$find(aes.name)) + guide.type <- if (length(aes.loc) == 1){ + scales$scales[[aes.loc]][["guide"]] + }else{ + "legend" + } + if(guide.type=="colourbar")guide.type <- "legend" + guides.args[[aes.name]] <- guide.type + } + guides.result <- do.call(ggplot2::guides, guides.args) + guides.list <- plyr::defaults(plot$guides, guides.result) + gdefs <- + ggplot2:::guides_train(scales = scales, + theme = theme, + guides = guides.list, + labels = plot$labels) + if (length(gdefs) != 0) { + gdefs <- ggplot2:::guides_merge(gdefs) + gdefs <- ggplot2:::guides_geom(gdefs, layers, default_mapping) + } else (ggplot2:::zeroGrob()) + names(gdefs) <- sapply(gdefs, function(i) i$title) + + ## adding the variable used to each LegendList + for(leg in seq_along(gdefs)) { + legend_type <- names(gdefs[[leg]]$key) + legend_type <- legend_type[legend_type != ".label"] + gdefs[[leg]]$legend_type <- legend_type + scale.list <- scales$scales[which(scales$find(legend_type))] + discrete.vec <- sapply(scale.list, inherits, "ScaleDiscrete") + is.discrete <- all(discrete.vec) + gdefs[[leg]]$is.discrete <- is.discrete + ## get the name of the legend/selection variable. + var.list <- list() + for(layer.i in seq_along(plot$layers)) { + L <- plot$layers[[layer.i]] + var.list[[layer.i]] <- L$mapping[legend_type] + } + unique.var.list <- unique(unlist(var.list)) + if(is.discrete){ + var.name <- unique.var.list[[1]] + if(length(unique.var.list) == 1 && is.symbol(var.name)){ + gdefs[[leg]]$selector <- paste(var.name) + }else{ + str(unique.var.list) + stop("need exactly 1 variable name ", + "(not constant, not language/expression) ", + "to create interactive discrete legend for aes ", + paste(legend_type, collapse=", ")) + } + } + + ## do not draw geoms which are constant: + geom.list <- gdefs[[leg]]$geoms + geom.data.list <- lapply(geom.list, "[[", "data") + geom.data.rows <- sapply(geom.data.list, nrow) + geom.unique.list <- lapply(geom.data.list, unique) + geom.unique.rows <- sapply(geom.unique.list, nrow) + is.ignored <- 1 < geom.data.rows & geom.unique.rows == 1 + gdefs[[leg]]$geoms <- geom.list[!is.ignored] + + ## Pass a geom.legend.list to be used by the + ## GetLegend function + geom.legend.list <- list() + for(geom.i in seq_along(gdefs[[leg]]$geoms)){ + data.geom.i <- gdefs[[leg]]$geoms[[geom.i]]$data + params.geom.i <- gdefs[[leg]]$geoms[[geom.i]]$params + size.geom.i <- gdefs[[leg]]$geoms[[geom.i]]$size + + suppressWarnings(draw.key.used <- + gdefs[[leg]]$geoms[[geom.i]]$draw_key( + data.geom.i, params.geom.i, size.geom.i) + ) + geom.legend <- class(draw.key.used)[[1]] + geom.legend.list <- c(geom.legend.list, geom.legend) + } + + ## Process names to be used by the CleanData function + convert.names.list <- list(points="point", segments="path", rect="polygon") + names.to.change <- geom.legend.list %in% names(convert.names.list) + geom.legend.list[names.to.change] <- + convert.names.list[unlist(geom.legend.list[names.to.change])] + + gdefs[[leg]]$geom.legend.list <- geom.legend.list + } + + ## Add a flag to specify whether or not breaks was manually + ## specified. If it was, then it should be respected. If not, and + ## the legend shows a numeric variable, then it should be reversed. + for(legend.name in names(gdefs)){ + key.df <- gdefs[[legend.name]]$key + aes.name <- names(key.df)[1] + scale.i <- which(scales$find(aes.name)) + if(length(scale.i) == 1){ + sc <- scales$scales[[scale.i]] + gdefs[[legend.name]]$breaks <- sc$breaks + } + } + + legend.list <- lapply(gdefs, getLegend) + ## Add a flag to specify whether or not there is both a color and a + ## fill legend to display. If so, we need to draw the interior of + ## the points in the color legend as the same color. + if(1 < length(legend.list)){ + is.color <- sapply(legend.list, function(L)"colour" %in% L$legend_type) + is.fill <- sapply(legend.list, function(L)"fill" %in% L$legend_type) + is.point <- sapply(legend.list, function(L)"point" %in% L$geoms) + has.both <- 2 == sum(is.point & (is.color | is.fill)) + if(has.both){ + for(legend.i in which(is.color)){ + entry.list <- legend.list[[legend.i]]$entries + for(entry.i in seq_along(entry.list)){ + entry <- entry.list[[entry.i]] + color.names <- grep("colour", names(entry), value=TRUE) + fill.names <- sub("colour", "fill", color.names) + entry[fill.names] <- "#FFFFFF" + legend.list[[legend.i]]$entries[[entry.i]] <- entry + } + } + } + } + legend.list[0 < sapply(legend.list, length)] +} diff --git a/R/animintHelpers.R b/R/animintHelpers.R new file mode 100644 index 000000000..ffe22795a --- /dev/null +++ b/R/animintHelpers.R @@ -0,0 +1,936 @@ +## Animint specific helper functions + +#' Add a showSelected aesthetic if legend is specified +#' @param meta meta object with all information +#' @param legend legend to scan for showSelected +#' @param L layer of the plot +#' @return L : Layer with additional mapping to new aesthetic +addShowSelectedForLegend <- function(meta, legend, L){ + for(legend.i in seq_along(legend)) { + one.legend <- legend[[legend.i]] + ## the name of the selection variable used in this legend. + s.name <- one.legend$selector + is.variable.name <- is.character(s.name) && length(s.name) == 1 + layer.has.variable <- s.name %in% names(L$data) + + if(is.variable.name && layer.has.variable) { + ## grabbing the variable from the data + var <- L$data[, s.name] + is.interactive.aes <- + grepl("showSelected|clickSelects", names(L$mapping)) + is.legend.var <- L$mapping == s.name + ## If s.name is used with another interactive aes, then do + ## not add any showSelected aesthetic for it. + var.is.interactive <- any(is.interactive.aes & is.legend.var) + if(!var.is.interactive){ + ## only add showSelected aesthetic if the variable is + ## used by the geom + type.vec <- one.legend$legend_type + if(any(type.vec %in% names(L$mapping))){ + type.str <- paste(type.vec, collapse="") + a.name <- paste0("showSelectedlegend", type.str) + L$mapping[[a.name]] <- as.symbol(s.name) + } + } + ## if selector.types has not been specified, create it + if(is.null(meta$selector.types)) { + meta$selector.types <- list() + } + ## if selector.types is not specified for this variable, set + ## it to multiple. + if(is.null(meta$selector.types[[s.name]])) { + meta$selector.types[[s.name]] <- "multiple" + meta$selectors[[s.name]]$type <- "multiple" + } + ## if first is not specified, create it + if(is.null(meta$first)) { + meta$first <- list() + } + ## if first is not specified, add all to first + if(is.null(meta$first[[s.name]])) { + u.vals <- unique(var) + } + ## Tell this selector that it has a legend somewhere in the + ## viz. (if the selector has no interactive legend and no + ## clickSelects, then we show the widgets by default). + meta$selectors[[s.name]]$legend <- TRUE + }#length(s.name) + }#legend.i + return(L) +} + + +## extract panel background and borders from theme.pars +get_bg <- function(pars, theme.pars) { + # if pars is not an empty list - occurs when using element_blank() + if(length(pars) > 0) { + + ## if elements are not specified, they inherit from theme.pars$rect + for(i in 1:length(pars)) { + if(is.null(pars[[i]])) pars[[i]] <- unname(theme.pars$rect[[i]]) + } + + # convert fill to RGB if necessary + if(!(is.rgb(pars$fill))) pars$fill <- unname(toRGB(pars$fill)) + # convert color to RGB if necessary + if(!(is.rgb(pars$colour))) pars$colour <- unname(toRGB(pars$colour)) + + # remove names (JSON file was getting confused) + pars <- lapply(pars, unname) + + } + pars +} + + +### function to extract grid info +get_grid <- function(pars, theme.pars, plot.meta, meta, built, major = T) { + # if pars is not an empty list - occurs when using element_blank() + if(length(pars) > 0) { + + ## if elements are not specified, they inherit from + ## theme.pars$panel.grid then from theme.pars$line + for(i in names(pars)) { + if(is.null(pars[[i]])) pars[[i]] <- + if(!is.null(theme.pars$panel.grid[[i]])) { + theme.pars$panel.grid[[i]] + } else { + theme.pars$line[[i]] + } + } + # convert colour to RGB if necessary + if(!is.rgb(pars$colour)) pars$colour <- unname(toRGB(pars$colour)) + + # remove names (JSON file was getting confused) + pars <- lapply(pars, unname) + } + + ## x and y locations + if(major) { + pars$loc$x <- as.list(built$panel$ranges[[1]]$x.major_source) + pars$loc$y <- as.list(built$panel$ranges[[1]]$y.major_source) + } else { + pars$loc$x <- as.list(built$panel$ranges[[1]]$x.minor_source) + pars$loc$y <- as.list(built$panel$ranges[[1]]$y.minor_source) + ## remove minor lines when major lines are already drawn + pars$loc$x <- pars$loc$x[ + !(pars$loc$x %in% plot.meta$grid_major$loc$x) + ] + pars$loc$y <- pars$loc$y[ + !(pars$loc$y %in% plot.meta$grid_major$loc$y) + ] + } + pars +} + + +#' Get unique axis labels for the plot +#' @param plot.meta contains axis labels +#' @return modified \code{plot.meta} with unique axis labels +getUniqueAxisLabels <- function(plot.meta){ + axis.info <- plot.meta[grepl("^axis[0-9]+$", names(plot.meta))] + plot.meta$xlabs <- as.list(unique(unlist(lapply(axis.info, "[", "xlab")))) + plot.meta$ylabs <- as.list(unique(unlist(lapply(axis.info, "[", "ylab")))) + return(plot.meta) +} + + +getPlotTitle <- function(plot.title, meta.title){ + if("element_blank"%in%attr(plot.title, "class")){ + return("") + } else { + return(meta.title) + } +} + + +getWidthAndHeight <- function(theme){ + options_list <- list() + for(wh in c("width", "height")){ + awh <- paste0("animint.", wh) + options_list[[wh]] <- if(awh %in% names(theme)){ + theme[[awh]] + }else{ + 400 + } + } + options_list +} + + +setUpdateAxes <- function(theme, options_list){ + update_axes <- "animint.update_axes" + if(update_axes %in% names(theme)){ + options_list$update_axes <- theme[[update_axes]] + } + options_list +} + + +hjust2anchor <- function(hjust){ + if(is.null(hjust))return(NULL) + stopifnot(is.numeric(hjust)) + trans <- + c("0"="start", + "0.5"="middle", + "1"="end") + hjust.str <- as.character(hjust) + is.valid <- hjust.str %in% names(trans) + if(all(is.valid)){ + ## as.character removes names. + as.character(trans[hjust.str]) + }else{ + print(hjust[!is.valid]) + stop("animint only supports hjust values 0, 0.5, 1") + } +} + + +#' Get all parameters for a layer +#' +#' @param l A single layer of the plot +#' @return All parameters in the layer +getLayerParams <- function(l){ + params <- c(l$geom_params, l$stat_params, l$aes_params, l$extra_params) + for(p.name in names(params)){ + if("chunk_vars" %in% names(params) && is.null(params[["chunk_vars"]])){ + params[["chunk_vars"]] <- character() + } + names(params[[p.name]]) <- NULL + ## Ignore functions. + if(is.function(params[[p.name]])){ + params[[p.name]] <- NULL + } + } + params +} + + +#' Filter out columns that do not need to be copied +#' +#' @param g Geom with columns +#' @param s.aes Selector aesthetics +#' @return Character vector of columns not to be copied +colsNotToCopy <- function(g, s.aes){ + group.not.specified <- ! "group" %in% names(g$aes) + n.groups <- length(unique(NULL)) + need.group <- c("violin", "step", "hex") + group.meaningless <- g$geom %in% c( + "abline", "blank", + ##"crossbar", "pointrange", #documented as unsupported + ## "rug", "dotplot", "quantile", "smooth", "boxplot", + ## "bin2d", "map" + "errorbar", "errorbarh", + ##"bar", "histogram", #? + "hline", "vline", + "jitter", "linerange", + "point", + "rect", "segment") + dont.need.group <- ! g$geom %in% need.group + remove.group <- group.meaningless || + group.not.specified && 1 < n.groups && dont.need.group + do.not.copy <- c( + if(remove.group)"group", + s.aes$showSelected$ignored, + s.aes$clickSelects$ignored) + do.not.copy +} + + +## Generate error for non-Identity Stat + showSelected +checkForNonIdentityAndSS <- function(stat.type, has.show, is.show, l, + g_classed, g_data_names, + aes_names){ + if(has.show && stat.type != "StatIdentity"){ + show.names <- aes_names[is.show] + data.has.show <- show.names %in% names(g.data) + signal <- if(all(data.has.show))warning else stop + print(l) + signal( + "showSelected does not work with ", + stat.type, + ", problem: ", + g$classed) + } +} + + +#' Flip axes in case of coord_flip +#' Switches column names. Eg. xmin to ymin, yntercept to xintercept etc. +#' @param col.names Column names which need to be switched +#' @return Column names with x and y axes switched +switch_axes <- function(col.names){ + for(elem in seq_along(col.names)){ + if(grepl("^x", col.names[elem])){ + col.names[elem] <- sub("^x", "y", col.names[elem]) + } else if(grepl("^y", col.names[elem])){ + col.names[elem] <- sub("^y", "x", col.names[elem]) + } + } + col.names +} + + +## Check if output type is linetype +is.linetype <- function(x){ + x <- tolower(x) + namedlinetype <- + x%in%c("blank", "solid", "dashed", + "dotted", "dotdash", "longdash", "twodash") + xsplit <- sapply(x, function(i){ + sum(is.na(strtoi(strsplit(i,"")[[1]],16)))==0 + }) + namedlinetype | xsplit +} + + +## Remove PANEL column from data if it has a single unique value +removeUniquePanelValue <- function(g.data, plot.has.panels){ + PANEL_vals <- unique(g.data[["PANEL"]]) + geom.has.one.panel <- length(PANEL_vals) == 1 + if(geom.has.one.panel && (!plot.has.panels)) { + g.data <- g.data[names(g.data) != "PANEL"] + } + g.data +} + + +#' Check plot.list for errors +#' +#' Check that plot.list is a list and every element is named +#' @param plot.list from \code{animint2dir} to check for errors +#' @return Throws an error for invalid values +checkPlotList <- function(plot.list){ + if (!is.list(plot.list)) + stop("plot.list must be a list of ggplots") + if (is.null(names(plot.list))) + stop("plot.list must be a named list") + if (any(names(plot.list)=="")) + stop("plot.list must have names with non-empty strings") + return(NULL) +} + + +#' Environment to store meta data +#' +#' Get a new environment to store meta-data. Used to alter state in the +#' lower-level functions +#' @return A new environment to store meta data +newEnvironment <- function(){ + meta <- new.env() + meta$plots <- list() + meta$geoms <- list() + meta$selectors <- list() + return(meta) +} + + +#' Check animation variable for errors +#' +#' @param timeVarList \code{plot.list$time} in \code{animint2dir} to check +#' for errors +#' @return \code{NULL} :Stops with an error for invalid input +checkAnimationTimeVar <- function(timeVarList){ + # Check if both ms duration and variable are present + if(!all(c("ms", "variable") %in% names(timeVarList))){ + stop("time option list needs ms, variable") + } + + ms <- timeVarList$ms + stopifnot(is.numeric(ms)) + stopifnot(length(ms)==1) + ## NOTE: although we do not use plot.list$ms for anything in the R + ## code, it is used to control the number of milliseconds between + ## animation frames in the JS code. + timeVar <- timeVarList$variable + stopifnot(is.character(timeVar)) + stopifnot(length(timeVar)==1) + return(NULL) +} + +#' Performs error checking on the plot for animint extensions +#' +#' @param p plot from \code{plot.list} to check for errors +#' @param plot_name plot name error check. Should be alphanumeric and should +#' begin with an alphabet +#' @return \code{NULL} :Stops with an error for invalid input +checkPlotForAnimintExtensions <- function(p, plot_name){ + # Check if plot is properly named + pattern <- "^[a-zA-Z][a-zA-Z0-9]*$" + if(!grepl(pattern, plot_name)){ + stop("ggplot names must match ", pattern) + } + + # Check layer by layer for proper extensions + for(L in p$layers){ + ## This code assumes that the layer has the complete aesthetic + ## mapping and data. TODO: Do we need to copy any global + ## values to this layer? + name.counts <- table(names(L$mapping)) + is.dup <- 1 < name.counts + if(any(is.dup)){ + print(L) + stop("aes names must be unique, problems: ", + paste(names(name.counts)[is.dup], collapse=", ")) + } + iaes <- selector.aes(L$mapping) + one.names <- with(iaes, c(clickSelects$one, showSelected$one)) + update.vars <- as.character(L$mapping[one.names]) + # if the layer has a defined data set + if(length(L$data) > 0) { + # check whether the variable is in that layer + has.var <- update.vars %in% names(L$data) + } else { + # check whether the variable is in the global data + has.var <- update.vars %in% names(p$data) + } + + if(!all(has.var)){ + print(L) + print(list(problem.aes=update.vars[!has.var], + data.variables=names(L$data))) + stop("data does not have interactive variables") + } + has.cs <- 0 < with(iaes$clickSelects, nrow(several) + length(one)) + has.href <- "href" %in% names(L$mapping) + if(has.cs && has.href){ + stop("aes(clickSelects) can not be used with aes(href)") + } + } + return(NULL) +} + + +## Compute domains of different subsets, to be used by update_scales +## in the renderer +compute_domains <- function(built_data, axes, geom_name, + vars, split_by_panel){ + # Different geoms will use diff columns to calculate domains for + # showSelected subsets. Eg. geom_bar will use 'xmin', 'xmax', 'ymin', + # 'ymax' etc. while geom_point will use 'x', 'y' + domain_cols <- list(bar=c(paste0(axes, "min"), paste0(axes, "max")), + ribbon=if(axes=="x"){c(axes)} + else{c(paste0(axes, "min"), paste0(axes, "max"))}, + rect=c(paste0(axes, "min"), paste0(axes, "max")), + tallrect=if(axes=="x") + {c(paste0("xmin"), paste0("xmax"))} + else{NULL}, + point=c(axes), + path=c(axes), + text=c(axes), + line=c(axes), + segment=c(axes, paste0(axes, "end"))) + use_cols <- domain_cols[[geom_name]] + if(is.null(use_cols)){ + warning(paste0("axis updates have not yet been implemented for geom_", + geom_name), call. = FALSE) + return(NULL) + }else if(!all(use_cols %in% names(built_data))){ + return(NULL) + } + domain_vals <- list() + inter_data <- built_data[[ vars[[1]] ]] + # If we have more than one showSelected vars, we need to compute + # every possible subset domain + if(length(vars) > 1){ + for(i in 2:length(vars)){ + inter_data <- interaction(inter_data, built_data[[vars [[i]] ]], + sep = "_") + } + } + # Split by PANEL only when specified, else use first value of PANEL + # It is a hack and must be handled in a better way + split_by <- if(split_by_panel){ + interaction(built_data$PANEL, inter_data) + }else{ + levels(inter_data) <- paste0(unique(built_data$PANEL[[1]]), + ".", levels(inter_data)) + inter_data + } + if(geom_name %in% c("point", "path", "text", "line")){ + # We suppress 'returning Inf' warnings when we compute a factor + # interaction that has no data to display + domain_vals[[use_cols[1]]] <- + suppressWarnings(lapply(split(built_data[[use_cols[1]]], + split_by), + range, na.rm=TRUE)) + }else if(geom_name %in% c("bar", "rect", "tallrect")){ + # Calculate min and max values of each subset separately + min_vals <- suppressWarnings(lapply(split(built_data[[use_cols[1]]], + split_by), + min, na.rm=TRUE)) + max_vals <- suppressWarnings(lapply(split(built_data[[use_cols[2]]], + split_by), + max, na.rm=TRUE)) + domain_vals <- list(mapply(c, min_vals, max_vals, SIMPLIFY = FALSE)) + }else if(geom_name %in% c("segment")){ + domain_vals[[use_cols[1]]] <- + suppressWarnings(lapply(split(built_data[, use_cols], split_by), + range, na.rm=TRUE)) + }else if(geom_name %in% c("ribbon")){ + if(axes=="x"){ + domain_vals[[use_cols[1]]] <- + suppressWarnings(lapply(split(built_data[[use_cols[1]]], + split_by), + range, na.rm=TRUE)) + }else{ + min_vals <- suppressWarnings(lapply(split(built_data[[use_cols[1]]], + split_by), + min, na.rm=TRUE)) + max_vals <- suppressWarnings(lapply(split(built_data[[use_cols[2]]], + split_by), + max, na.rm=TRUE)) + domain_vals <- list(mapply(c, min_vals, max_vals, SIMPLIFY = FALSE)) + } + } + domain_vals +} + +## Out of all the possible geoms, get the min/max value which will +## determine the domain to be used in the renderer +get_domain <- function(subset_domains){ + use_domain <- list() + ## ggplot gives a margin of 5% at all four sides which does not + ## have any plotted data. So axis ranges are 10% bigger than the + ## actual ranges of data. We do the same here + extra_margin = 0.05 + for(i in unique(unlist(lapply(subset_domains, names)))){ + all_vals <- lapply(subset_domains, "[[", i) + all_vals <- all_vals[!sapply(all_vals, is.null)] + min_val <- min(sapply(all_vals, "[[", 1)) + max_val <- max(sapply(all_vals, "[[", 2)) + # We ignore non finite values that may have creeped in while + # calculating all possible subset domains + if(all(is.finite(c(max_val, min_val)))){ + use_domain[[i]] <-if(max_val - min_val > 0){ + c(min_val - (extra_margin *(max_val-min_val)), + max_val + (extra_margin *(max_val-min_val))) + }else{ + # If min_val and max_val are same, return a range equal to + # the value + warning("some data subsets have only a single data value to plot", + call. = FALSE) + return_dom <- c(min_val - (0.5 * min_val), max_val + (0.5 * max_val)) + if(min_val == 0){ + # if min_val = max_val = 0, return a range (-1, 1) + return_dom <- c(-1, 1) + } + return_dom + } + }else{ + warning("some data subsets have no data to plot", call. = FALSE) + } + } + use_domain +} + +## get axis ticks and major/minor grid lines for updating plots +get_ticks_gridlines <- function(use_domain){ + gridlines <- list() + for (i in seq_along(use_domain)){ + all_lines <- scales::pretty_breaks(n=10)(use_domain[[i]]) + if(length(all_lines) > 0){ + # make sure grid lines are not outside plot domain + if(use_domain[[i]][1] > all_lines[[1]]){ + all_lines <- all_lines[2:length(all_lines)] + } + if(use_domain[[i]][2] < all_lines[[length(all_lines)]]){ + all_lines <- all_lines[1:(length(all_lines)-1)] + } + # Every second grid line is minor, rest major + # Major grid lines are also used for drawing axis ticks + # Eg. If all_lines = 1:10 + # minor grid lines = 1, 3, 5, 7, 9 + # major grid lines = 2, 4, 6, 8, 10 + majors <- all_lines[c(FALSE, TRUE)] + minors <- all_lines[c(TRUE, FALSE)] + gridlines[[ names(use_domain)[[i]] ]] <- list(minors, majors) + } + } + gridlines +} + + +#' Issue warnings for selectors +#' @param geoms \code{geoms} to check for warnings +#' @param selector.aes selectors for each geom +#' @param duration animation variable information to check for \code{key} value +#' @return \code{NULL} +issueSelectorWarnings <- function(geoms, selector.aes, duration){ + for(g.name in names(geoms)){ + g.info <- geoms[[g.name]] + g.selectors <- selector.aes[[g.name]] + show.vars <- g.info$aes[g.selectors$showSelected$one] + duration.vars <- names(duration) + show.with.duration <- show.vars[show.vars %in% duration.vars] + no.key <- ! "key" %in% names(g.info$aes) + if(length(show.with.duration) && no.key){ + warning( + "to ensure that smooth transitions are interpretable, ", + "aes(key) should be specifed for geoms with aes(showSelected=", + show.with.duration[1], + "), problem: ", g.name) + } + } + return(NULL) +} + +#' Gives a unique name to each layer in \code{saveLayer} +#' @param L layer in saveLayer to be named +#' @param geom_num the number of the layer to be saved +#' @param p.name the name of the plot to which the layer belongs +#' @return a unique name for the layer +getLayerName <- function(L, geom_num, p.name){ + # carson's approach to getting layer types + ggtype <- function (x, y = "geom") { + sub(y, "", tolower(class(x[[y]])[1])) + } + layer_name <- sprintf("geom%d_%s_%s", + geom_num, ggtype(L), p.name) + layer_name +} + + +#' Issue warnings for non interactive plots where there is only one +#' showSelected value +#' @param selectors selectors to check for warnings +#' @return \code{NULL} +checkSingleShowSelectedValue <- function(selectors){ + if(length(selectors) > 0){ + n.levels <- sapply(selectors, function(s.info)length(s.info$levels)) + one.level <- n.levels == 1 + has.legend <- sapply(selectors, function(s.info)isTRUE(s.info$legend)) + is.trivial <- one.level & (!has.legend) + if(any(is.trivial)){ + ## With the current compiler that has already saved the tsv files + ## by now, we can't really make this data viz more efficient by + ## ignoring this trivial selector. However we can warn the user so + ## that they can remove this inefficient showSelected. + warning("showSelected variables with only 1 level: ", + paste(names(selectors)[is.trivial], collapse=", ")) + } + } + return(NULL) +} + + +#' Set plot width and height for all plots +#' @param meta meta object with all information +#' @return \code{NULL}. Sizes are stored in meta object +setPlotSizes <- function(meta, AllPlotsInfo){ + # Set both width and height + for(d in c("width","height")){ + size <- meta[[d]] + if(is.list(size)){ + warning("option ", d, " is deprecated, ", + "use ggplot()+theme_animint(", d, + "=", size[[1]], + ") instead") + if(is.null(names(size))){ #use this size for all plots. + for(plot.name in names(AllPlotsInfo)){ + AllPlotsInfo[[plot.name]]$options[[d]] <- size[[1]] + } + }else{ #use the size specified for the named plot. + for(plot.name in names(size)){ + if(plot.name %in% names(AllPlotsInfo)){ + AllPlotsInfo[[plot.name]]$options[[d]] <- size[[plot.name]] + }else{ + stop("no ggplot named ", plot.name) + } + } + } + } + } + return(NULL) +} + + + +#' Merge a list of data frames. +#' @param dfs list of data frames +#' @return data frame +merge_recurse <- function(dfs){ + label.vec <- unique(unlist(lapply(dfs, function(df)paste(df$label)))) + result <- data.frame(row.names=label.vec) + for(df in dfs){ + df.label <- paste(df$label) + for(col.name in names(df)){ + result[df.label, col.name] <- df[[col.name]] + } + } + result +} + + +#' Function to get legend information for each scale +#' @param mb single entry from ggplot2:::guides_merge() list of legend data +#' @return list of legend information, NULL if guide=FALSE. +getLegend <- function(mb){ + guidetype <- mb$name + + ## The main idea of legends: + + ## 1. Here in getLegend I export the legend entries as a list of + ## rows that can be used in a data() bind in D3. + + ## 2. In add_legend in the JS code I create a for every + ## legend, and then I bind the legend entries to ,
, and + ## elements. + cleanData <- function(data, key, geom, params) { + nd <- nrow(data) + nk <- nrow(key) + if (nd == 0) return(data.frame()); # if no rows, return an empty df. + if ("guide" %in% names(params)) { + if (params[["guide"]] == "none") return(data.frame()); # if no guide, return an empty df + } + if (nd != nk) warning("key and data have different number of rows") + if (!".label" %in% names(key)) return(data.frame()); # if there are no labels, return an empty df. + data$`.label` <- key$`.label` + data <- data[, which(colSums(!is.na(data)) > 0)] # remove cols that are entirely na + if("colour" %in% names(data)) data[["colour"]] <- toRGB(data[["colour"]]) # color hex values + if("fill" %in% names(data)) data[["fill"]] <- toRGB(data[["fill"]]) # fill hex values + names(data) <- paste0(geom, names(data))# aesthetics by geom + names(data) <- gsub(paste0(geom, "."), "", names(data), fixed=TRUE) # label isn't geom-specific + data$label <- paste(data$label) # otherwise it is AsIs. + data + } + dataframes <- mapply(function(i, j) cleanData(i$data, mb$key, j, i$params), + mb$geoms, mb$geom.legend.list, SIMPLIFY = FALSE) + dataframes <- dataframes[which(sapply(dataframes, nrow)>0)] + # Check to make sure datframes is non-empty. If it is empty, return NULL. + if(length(dataframes)>0) { + data <- merge_recurse(dataframes) + } else return(NULL) + label.num <- suppressWarnings({ + as.numeric(data$label) + }) + ## mb$breaks could be a vector of values to use, NULL, or an empty + ## list with class "waiver" + breaks.specified <- length(mb$breaks) + entry.order <- if(breaks.specified || anyNA(label.num)){ + 1:nrow(data) + }else{ + nrow(data):1 + } + data <- lapply(entry.order, function(i) as.list(data[i,])) + if(guidetype=="none"){ + NULL + }else{ + list(guide = guidetype, + geoms = unlist(mb$geom.legend.list), + title = mb$title, + class = if(mb$is.discrete)mb$selector else mb$title, + selector = mb$selector, + is_discrete= mb$is.discrete, + legend_type = mb$legend_type, + entries = data) + } +} + + +##' Save the common columns for each tsv to one chunk +##' @param built data.frame of built data. +##' @param vars character vector of chunk variable names to split on. +##' @param aes.list a character vector of aesthetics. +##' @return a list of common and varied data to save, or NULL if there is +##' no common data. +getCommonChunk <- function(built, chunk.vars, aes.list){ + if(length(chunk.vars) == 0){ + return(NULL) + } + if(! "group" %in% names(aes.list)){ + ## user did not specify group, so do not use any ggplot2-computed + ## group for deciding common data. + built$group <- NULL + } + + ## Remove columns with all NA values + ## so that common.not.na is not empty + ## due to the plot's alpha, stroke or other columns + all.nas <- sapply(built, function(x){all(is.na(x))}) + built <- built[, !all.nas] + + ## Treat factors as characters, to avoid having them be coerced to + ## integer later. + for(col.name in names(built)){ + if(is.factor(built[, col.name])){ + built[, col.name] <- paste(built[, col.name]) + } + } + + ## If there is only one chunk, then there is no point of making a + ## common data file. + chunk.rows.tab <- table(built[, chunk.vars]) + if(length(chunk.rows.tab) == 1) return(NULL) + + ## If there is no group column, and all the chunks are the same + ## size, then add one based on the row number. + if(! "group" %in% names(built)){ + chunk.rows <- chunk.rows.tab[1] + same.size <- chunk.rows == chunk.rows.tab + order.args <- lapply(chunk.vars, function(order.col)built[[order.col]]) + built <- built[do.call(order, order.args),] + if(all(same.size)){ + built$group <- 1:chunk.rows + }else{ + ## do not save a common chunk file. + return(NULL) + } + } + + built.by.group <- split(built, built$group) + group.tab <- table(built[, c("group", chunk.vars)]) + each.group.same.size <- apply(group.tab, 1, function(group.size.vec){ + group.size <- group.size.vec[1] + if(all(group.size == group.size.vec)){ + ## groups are all this size. + group.size + }else{ + ## groups not the same size. + 0 + } + }) + + checkCommon <- function(col.name){ + for(group.name in names(built.by.group)){ + data.vec <- built.by.group[[group.name]][[col.name]] + if(group.size <- each.group.same.size[[group.name]]){ + not.same.value <- data.vec != data.vec[1:group.size] + if(any(not.same.value, na.rm=TRUE)){ + ## if any data values are different, then this is not a + ## common column. + return(FALSE) + } + }else{ + ## this group has different sizes in different chunks, so the + ## only way that we can make common data is if there is only + ## value. + value.tab <- table(data.vec) + if(length(value.tab) != 1){ + return(FALSE) + } + } + } + TRUE + } + + all.col.names <- names(built) + col.name.vec <- all.col.names[!all.col.names %in% chunk.vars] + is.common <- sapply(col.name.vec, checkCommon) + + ## TODO: another criterion could be used to save disk space even if + ## there is only 1 chunk. + n.common <- sum(is.common) + if(is.common[["group"]] && 2 <= n.common && n.common < length(is.common)){ + common.cols <- names(is.common)[is.common] + group.info.list <- list() + for(group.name in names(built.by.group)){ + one.group <- built.by.group[[group.name]] + group.size <- each.group.same.size[[group.name]] + if(group.size == 0){ + group.size <- 1 + } + group.common <- one.group[, common.cols] + ## Instead of just taking the first chunk for this group (which + ## may have NA), look for the chunk which has the fewest NA. + is.na.vec <- apply(is.na(group.common), 1, any) + is.na.mat <- matrix(is.na.vec, group.size) + group.i <- which.min(colSums(is.na.mat)) + offset <- (group.i-1)*group.size + group.info.list[[group.name]] <- group.common[(1:group.size)+offset, ] + } + group.info.common <- do.call(rbind, group.info.list) + common.unique <- unique(group.info.common) + ## For geom_polygon and geom_path we may have two rows that should + ## both be kept (the start and the end of each group may be the + ## same if the shape is closed), so we define common.data as all + ## of the rows (common.not.na) in that case, and just the unique + ## data per group (common.unique) in the other case. + data.per.group <- table(common.unique$group) + common.data <- if(all(data.per.group == 1)){ + common.unique + }else{ + group.info.common + } + varied.df.list <- split.x(na.omit(built), chunk.vars) + varied.cols <- c("group", names(is.common)[!is.common]) + varied.data <- varied.chunk(varied.df.list, varied.cols) + return(list(common=na.omit(common.data), + varied=varied.data)) + } +} + + +##' Extract subset for each data.frame in a list of data.frame +##' @param df.or.list a data.frame or a list of data.frame. +##' @param cols cols that each data.frame would keep. +##' @return list of data.frame. +varied.chunk <- function(df.or.list, cols){ + if(is.data.frame(df.or.list)){ + df <- df.or.list[, cols, drop = FALSE] + u.df <- unique(df) + group.counts <- table(u.df$group) + if(all(group.counts == 1)){ + u.df + }else{ + df + } + } else{ + lapply(df.or.list, varied.chunk, cols) + } +} + + +##' Split data.frame into recursive list of data.frame. +##' @param x data.frame. +##' @param vars character vector of variable names to split on. +##' @return recursive list of data.frame. +split.x <- function(x, vars){ + if(length(vars)==0)return(x) + if(is.data.frame(x)){ + + ## Remove columns with all NA values + ## so that x is not empty due to + ## the plot's alpha, stroke or other columns + all.nas <- sapply(x, function(col.m){all(is.na(col.m))}) + x <- x[, !all.nas] + + # rows with NA should not be saved + x <- na.omit(x) + if(length(vars) == 1){ + split(x[names(x) != vars], x[vars], drop = TRUE) + }else{ + use <- vars[1] + rest <- vars[-1] + df.list <- split(x[names(x) != use], x[use], drop = TRUE) + split.x(df.list, rest) + } + }else if(is.list(x)){ + lapply(x, split.x, vars) + }else{ + str(x) + stop("unknown object") + } +} + + +##' Split data set into chunks and save them to separate files. +##' @param x data.frame. +##' @param meta environment. +##' @return recursive list of chunk file names. +##' @author Toby Dylan Hocking +saveChunks <- function(x, meta){ + if(is.data.frame(x)){ + this.i <- meta$chunk.i + csv.name <- sprintf("%s_chunk%d.tsv", meta$g$classed, this.i) + write.table(x, file.path(meta$out.dir, csv.name), quote=FALSE, + row.names=FALSE, sep="\t") + meta$chunk.i <- meta$chunk.i + 1L + this.i + }else if(is.list(x)){ + lapply(x, saveChunks, meta) + }else{ + str(x) + stop("unknown object") + } +} diff --git a/R/facets.R b/R/facets.R new file mode 100644 index 000000000..113dd38e8 --- /dev/null +++ b/R/facets.R @@ -0,0 +1,142 @@ + +# Determine titles to put on facet panels. +# The implementation here is a modified version of ggplot2:::facet_strips. +getStrips <- function(facet, panel, ...) + # ... is a placeholder at the moment in case we want to implement + # themes or special options later + UseMethod("getStrips") + +getStrips.grid <- function(facet, panel, ...) { + npanels <- nrow(panel$layout) + # preallocate strip labels to a default of "" + strips.empty <- strips.right <- strips.top <- rep("", npanels) + # right strips in a grid are only drawn on the last column + row_vars <- unique(panel$layout[names(facet$rows)]) + strips.right[with(panel$layout, COL == max(COL))] <- + build_strip(panel, row_vars, facet$labeller, side = "right", ...) + # top strips in a grid layout are only drawn on the first row + col_vars <- unique(panel$layout[names(facet$cols)]) + strips.top[panel$layout$ROW == 1] <- + build_strip(panel, col_vars, facet$labeller, side = "top", ...) + strips <- list(right = as.list(strips.right), top = as.list(strips.top)) + # the right/top element should exist if there are non-trivial labels + # strips <- strips[sapply(strips, function(x) !identical(x, strips.empty))] + strips$n <- list(top = 1, right = 1) + strips +} + +build_strip <- function(panel, label_df, labeller, side = "right", ...) { + side <- match.arg(side, c("top", "left", "bottom", "right")) + labeller <- match.fun(labeller) + # No labelling data, so return empty string? + if (plyr::empty(label_df)) { + return("") + } + # Create matrix of labels + labels <- matrix(list(), nrow = nrow(label_df), ncol = ncol(label_df)) + labels <- lapply(labeller(label_df), cbind) + labels <- do.call("cbind", labels) + + # unlike ggplot2, we collapse "layers" of strips into 1 layer + apply(labels, 1, paste, collapse = "; ") +} + +getStrips.wrap <- function(facet, panel, ...) { + labels_df <- panel$layout[names(facet$facets)] + labels_df[] <- plyr::llply(labels_df, format, justify = "none") + # facet_wrap labels always go on top + # we return a list so p_info.strips is always an object (on the JS side) + strips <- list(top = apply(labels_df, 1, paste, collapse = ", "), right = list("")) + # strips <- strips[!identical(strips$top, rep("", nrow(panel$layout)))] + strips$n <- list(top = max(panel$layout$ROW), right = 0) + strips +} + +getStrips.null <- function(facet, panel, ...) { + list(top = list(""), right = list(""), n = list(top = 0, right = 0)) +} + +# Attach AXIS_X/AXIS_Y columns to the panel layout if +# facet_grids is used. +# Currently every axis is rendered, +# but this could be helpful if we decide not to that. +flag_axis <- function(facet, layout) + UseMethod("flag_axis") + +flag_axis.grid <- function(facet, layout) { + # 'grid rules' are to draw y-axis on panels with COL == 1 + # and ROW == max(ROW). + layout$AXIS_Y <- layout$COL == 1 + layout$AXIS_X <- layout$ROW == max(layout$ROW) + layout +} + +flag_axis.wrap <- function(facet, layout) { + if (sum(grepl("^AXIS_[X-Y]$", names(layout))) != 2) + stop("Expected 'AXIS_X' and 'AXIS_Y' to be in panel layout") + layout +} + +flag_axis.null <- function(facet, layout) { + cbind(layout, AXIS_X = TRUE, AXIS_Y = TRUE) +} + +# TODO: how to 'train_layout' for non-cartesian coordinates? +# https://github.com/hadley/ggplot2/blob/dfcb56ec067910e1a3a04693d8f1e146cc7fb796/R/coord-.r + +train_layout <- function(facet, coord, layout, ranges) { + npanels <- dim(layout)[1] + nrows <- max(layout$ROW) + ncols <- max(layout$COL) + ydiffs <- sapply(ranges, function(z) diff(z$y.range)) + xdiffs <- sapply(ranges, function(z) diff(z$x.range)) + # if x or y scale is 'free', then ignore the ratio + if (length(unique(xdiffs)) > 1 || length(unique(ydiffs)) > 1) + coord$ratio <- NULL + has.ratio <- !is.null(coord$ratio) + layout$coord_fixed <- has.ratio + if (has.ratio) { + spaces <- fixed_spaces(ranges, coord$ratio) + layout <- cbind(layout, width_proportion = spaces$x, height_proportion = spaces$y) + layout$width_proportion <- layout$width_proportion/ncols + layout$height_proportion <- layout$height_proportion/nrows + } else { + vars <- NULL + if (isTRUE(facet$space_free$x)) vars <- c(vars, "x") + if (isTRUE(facet$space_free$y)) vars <- c(vars, "y") + if (is.null(vars)) { # fill the entire space and give panels equal area + layout <- cbind(layout, width_proportion = rep(1/ncols, npanels), + height_proportion = rep(1/nrows, npanels)) + } else { #special handling for 'free' space + for (xy in vars) { + u.type <- toupper(xy) + l.type <- tolower(xy) + scale.type <- paste0("SCALE_", u.type) + range.type <- paste0(l.type, ".range") + space.type <- paste0("SPACE_", u.type) + vals <- layout[[scale.type]] + uv <- unique(vals) + diffs <- sapply(ranges[which(vals %in% uv)], + function(x) { diff(x[[range.type]]) }) + udiffs <- unique(diffs) + # decide the proportion of the height/width each scale deserves based on the range + props <- data.frame(tmp1 = uv, tmp2 = udiffs / sum(udiffs)) + names(props) <- c(scale.type, space.type) + layout <- plyr::join(layout, props, by = scale.type) + } + names(layout) <- gsub("SPACE_X", "width_proportion", names(layout), fixed = TRUE) + names(layout) <- gsub("SPACE_Y", "height_proportion", names(layout), fixed = TRUE) + } + } + layout +} + +# fixed cartesian coordinates (on a 0-1 scale) +# inspired from https://github.com/hadley/ggplot2/blob/dfcb56ec067910e1a3a04693d8f1e146cc7fb796/R/coord-fixed.r#L34-36 +fixed_spaces <- function(ranges, ratio = 1) { + aspect <- sapply(ranges, + function(z) diff(z$y.range) / diff(z$x.range) * ratio) + spaces <- list(y = aspect) + spaces$x <- 1/spaces$y + lapply(spaces, function(z) min(z, 1)) +} diff --git a/R/geoms.R b/R/geoms.R new file mode 100644 index 000000000..46aa1ee00 --- /dev/null +++ b/R/geoms.R @@ -0,0 +1,285 @@ +#' ggplot2 geom with xmin and xmax aesthetics that covers the entire y range, useful for clickSelects background elements. +#' @param mapping aesthetic mapping +#' @param data data set +#' @param stat statistic mapping, defaults to identity +#' @param position position mapping, defaults to identity +#' @param ... other arguments +#' @return ggplot2 layer +#' @export +#' @example inst/examples/breakpoints.R +geom_tallrect <- function(mapping = NULL, data = NULL, + stat = "identity", position = "identity", + ..., + na.rm = FALSE, + show.legend = NA, + inherit.aes = TRUE) { + ggplot2::layer( + geom = GeomTallRect, + data = data, + mapping = mapping, + stat = stat, + position = position, + show.legend = show.legend, + inherit.aes = inherit.aes, + params = list( + na.rm = na.rm, + ... + ) + ) +} + +GeomTallRect <- ggplot2::ggproto("GeomTallRect", ggplot2::Geom, + default_aes = ggplot2::aes(colour = "grey35", + fill = "grey35", + size = 0.5, + linetype = 1, + alpha = 0.5), + + required_aes = c("xmin", "xmax"), + + draw_panel = function(self, data, + panel_scales, coord) { + coords <- coord$transform(data, panel_scales) + ymax <- grid::unit(1, "npc") + ymin <- grid::unit(0, "npc") + grid::rectGrob( + coords$xmin, ymax, + width = coords$xmax - coords$xmin, + height = ymax - ymin, + default.units = "native", + just = c("left", "top"), + gp = grid::gpar( + col = coords$colour, + fill = scales::alpha(coords$fill, + coords$alpha), + lwd = coords$size * .pt, + lty = coords$linetype, + lineend = "butt" + ) + ) + }, + + draw_key = ggplot2::draw_key_rect +) + + +#' ggplot2 geom with ymin and ymax aesthetics that covers the entire x range, useful for clickSelects background elements. +#' @param mapping aesthetic mapping +#' @param data data set +#' @param stat statistic mapping, defaults to identity +#' @param position position mapping, defaults to identity +#' @param ... other arguments +#' @return ggplot2 layer +#' @export +#' @examples +#' \dontrun{ +#' source(system.file("examples/WorldBank.R", package = "animint")) +#' } +geom_widerect <- function(mapping = NULL, data = NULL, + stat = "identity", position = "identity", + ..., + na.rm = FALSE, + show.legend = NA, + inherit.aes = TRUE) { + ggplot2::layer( + geom = GeomWideRect, + data = data, + mapping = mapping, + stat = stat, + position = position, + show.legend = show.legend, + inherit.aes = inherit.aes, + params = list( + na.rm = na.rm, + ... + ) + ) +} + +GeomWideRect <- ggplot2::ggproto("GeomWideRect", ggplot2::Geom, + default_aes = ggplot2::aes(colour = "grey35", + fill = "grey35", + size = 0.5, + linetype = 1, + alpha = 0.5), + + required_aes = c("ymin", "ymax"), + + draw_panel = function(self, data, + panel_scales, coord) { + coords <- coord$transform(data, panel_scales) + xmax <- grid::unit(1, "npc") + xmin <- grid::unit(0, "npc") + grid::rectGrob( + xmin, coords$ymin, + width = xmax - xmin, + height = coords$ymax - coords$ymin, + default.units = "native", + just = c("left", "bottom"), + gp = grid::gpar( + col = coords$colour, + fill = scales::alpha(coords$fill, + coords$alpha), + lwd = coords$size * .pt, + lty = coords$linetype, + lineend = "butt" + ) + ) + }, + + draw_key = ggplot2::draw_key_rect +) + +#' Make a clickSelects geom_tallrect that completely tiles the x +#' range. This makes it easy to construct tallrects for the common +#' case of selecting a particular x value. +#' @param data data.frame to analyze for unique x.name values. +#' @param x.name variable to be used for x, clickSelects. +#' @param even Logical parameter, should tallrects be of even width? +#' @param alpha transparency of a selected tallrect, default 1/2. +#' @param ... passed to geom_tallrect. +#' @return a geom_tallrect layer. +#' @author Toby Dylan Hocking +#' @export +make_tallrect <- function(data, x.name, even=FALSE, alpha=1/2, ...){ + make_tallrect_or_widerect( + "x", geom_tallrect, data, x.name, even, alpha, ...) +} + +#' Make a clickSelects geom_widerect that completely tiles the y +#' range. This makes it easy to construct widerects for the common +#' case of selecting a particular y value. +#' @param data data.frame to analyze for unique y.name values. +#' @param y.name variable to be used for y, clickSelects. +#' @param even Logical parameter, should widerects be of even width? +#' @param alpha transparency of a selected widerect, default 1/2. +#' @param ... passed to geom_widerect. +#' @return a geom_widerect layer. +#' @author Toby Dylan Hocking +#' @export +make_widerect <- function(data, y.name, even=FALSE, alpha=0.5, ...){ + make_tallrect_or_widerect( + "y", geom_widerect, data, y.name, even, alpha, ...) +} + +#' Make a clickSelects geom_widerect or geom_tallrect that completely +#' tiles the x or y range. This function is used internally by +#' make_tallrect or make_widerect, which are more user-friendly. +#' @param aes.prefix "x" or "y" +#' @param geom_xrect geom_tallrect or geom_widerect +#' @param data data.frame to analyze for unique var.name values. +#' @param var.name variable to be used for clickSelects +#' @param even Logical parameter, should xrects be of even width? +#' @param alpha transparency of a selected xrect, default 1/2. +#' @param ... passed to geom_xrect +#' @param data.fun called on data passed to geom_xrect(aes(..), +#' data.fun(df)) this is useful in facetted plots, for adding +#' columns to the data.frame, if you want that geom in only one +#' panel. +#' @return a geom_xrect layer +#' @author Toby Dylan Hocking +#' @export +make_tallrect_or_widerect <- function(aes.prefix, geom_xrect, data, var.name, even=FALSE, alpha=0.5, ..., data.fun=identity){ + stopifnot(is.character(aes.prefix)) + stopifnot(length(aes.prefix)==1) + stopifnot(aes.prefix %in% c("x", "y")) + stopifnot(is.function(geom_xrect)) + data <- as.data.frame(data) + stopifnot(is.character(var.name)) + stopifnot(length(var.name)==1) + x <- data[, var.name] + stopifnot(is.numeric(x)) + stopifnot(is.logical(even)) + stopifnot(length(even)==1) + stopifnot(is.numeric(alpha)) + stopifnot(length(alpha)==1) + stopifnot(is.function(data.fun)) + vals <- sort(unique(x)) + Delta <- if(even) rep(ggplot2::resolution(vals), length(vals)-1)/2 else diff(vals)/2 + breaks <- c(vals[1] - Delta[1], + vals[-1] - Delta, + vals[length(vals)]+Delta[length(Delta)]) + stopifnot(length(breaks) == length(vals)+1) + df <- data.frame(vals, + min=breaks[-length(breaks)], + max=breaks[-1]) + geom.df <- expand.grid(click.i=1:nrow(df), show.i=1:nrow(df)) + geom.df$click.val <- df[geom.df$click.i, "vals"] + geom.df$show.val <- df[geom.df$show.i, "vals"] + geom.df$var <- var.name + geom.df$key <- with(geom.df, ifelse( + click.val==show.val, 1, + paste(click.val, show.val))) + aes.string.args <- list() + aes.string.args[["clickSelects.variable"]] <- "var" + aes.string.args[["clickSelects.value"]] <- "click.val" + aes.string.args[["showSelected.variable"]] <- "var" + aes.string.args[["showSelected.value"]] <- "show.val" + aes.string.args[["key"]] <- "key" + for(suffix in c("min", "max")){ + aes.str <- paste0(aes.prefix, suffix) + geom.df[[suffix]] <- df[geom.df$click.i, suffix] + aes.string.args[[aes.str]] <- suffix + } + a <- do.call(aes_string, aes.string.args) + geom_xrect(a, data.fun(geom.df), alpha=alpha, ...) +} + +#' Convenience function for an interactive bar that might otherwise be +#' created using stat_summary(geom="bar"). +#' @param data data.frame to analyze for unique x.name values. +#' @param x.name variable to be used for x, clickSelects. +#' @param alpha transparency of selected bar, default 1. +#' @return a geom_bar layer. +#' @author Toby Dylan Hocking +#' @export +make_bar <- function(data, x.name, alpha=1){ + data <- as.data.frame(data) + stopifnot(is.character(x.name)) + stopifnot(length(x.name)==1) + x <- data[,x.name] + stopifnot(is.numeric(x)) + stat_summary(aes_string(x=x.name, y=x.name, clickSelects=x.name), + data=data, alpha=alpha, fun.y=length, geom="bar") +} + +#' Convenvience function for a showSelected plot label. +#' @param data data.frame of relevant data +#' @param x x coordinate of label position +#' @param y y coordinate of label position +#' @param label.var variable matching showSelected, used to obtain label value +#' @param format String format for label. Use \%d, \%f, etc. to insert relevant label.var value. +#' @return a geom_text layer. +#' @author Toby Dylan Hocking +#' @export +make_text <- function(data, x, y, label.var, format=NULL){ + data <- as.data.frame(data) + stopifnot(length(x)==1) + stopifnot(length(y)==1) + ## TODO: position based on the data? + ## if(is.character(x) && x %in% names(data)){ + ## x <- data[,x] + ## x <- (min(x)+max(x))/2 + ## } + ## if(is.character(y) && y %in% names(data)){ + ## y <- max(data[,y]) + ## } + data <- unique(data[,label.var,drop=FALSE]) + data$label <- data[,label.var] + data$x <- x + data$y <- y + if(is.null(format)){ + data$label <- as.character(data$label) + format <- paste(label.var,"= %s") + } + if(is.character(format)){ + fstring <- format + format <- function(val){ + sprintf(fstring, val) + } + } + stopifnot(is.function(format)) + data$label <- format(data$label) + a <- aes_string(x="x",y="y",label="label",showSelected=label.var) + geom_text(a, data) +} diff --git a/R/gist.R b/R/gist.R new file mode 100644 index 000000000..59f261172 --- /dev/null +++ b/R/gist.R @@ -0,0 +1,80 @@ +#' Convert a list of ggplots to an interactive animation and post files as a gist +#' +#' Before using this function set your appropriate 'github.username' and 'github.password' \link{options} +#' +#' @param plot.list a named list of ggplots and option lists. +#' @param description Brief description of gist. +#' This becomes the plot title on the bl.ocks/username page. +#' @param browse logical. Prompt browser to view viz on \url{http://bl.ocks.org} +#' @param ... options passed onto \code{animint2dir} and \code{gistr::gist_create} +#' @export +#' +#' @examples +#' \dontrun{ +#' library(animint) +#' iris$id <- 1:nrow(iris) +#' viz <- list(petal=ggplot()+ +#' geom_point(aes(Petal.Width, Petal.Length, fill=Species, +#' clickSelects=id), data=iris), +#' sepal=ggplot()+ +#' geom_point(aes(Sepal.Width, Sepal.Length, fill=Species, +#' clickSelects=id), data=iris)) +#' animint2gist(viz, description = "My animint plot") +#' } +animint2gist <- function(plot.list, description = plot.list$title, + browse = TRUE, ...) { + if (!is.character(description) || length(description) == 0) description <- "" + if (length(description) > 1) description <- description[[1]] + res <- animint2dir(plot.list, open.browser = FALSE, ...) + if (!requireNamespace("gistr")) { + stop("Please run \n", + "devtools::install_github('rOpenSci/gistr')", + "before using this function") + } + # use a flat file structure! + vendor.path <- file.path(res$out.dir, "vendor") + vendor.files <- list.files(vendor.path) + vendor.path.files <- file.path(vendor.path, vendor.files) + copied <- file.copy(vendor.path.files, file.path(res$out.dir, vendor.files)) + file.remove(vendor.path.files) + file.remove(vendor.path) + # reflect script path in index.html to reflect the change in file structure + index.file <- file.path(res$out.dir, "index.html") + html <- readLines(index.file) + html <- gsub("vendor/", "", html) + cat(html, file = index.file, sep = "\n") + ## Figure out which files to post. + all.files <- Sys.glob(file.path(res$out.dir, "*")) + all.file.info <- file.info(all.files) + is.empty <- all.file.info$size == 0 + is.tilde <- grepl("~$", all.files) + is.png <- grepl("[.]png$", all.files) + is.ignored <- all.file.info$isdir | is.empty | is.tilde + ## TODO: delete the next line when gist_create can upload PNGs. + is.ignored <- is.ignored | is.png + to.post <- all.files[!is.ignored] + if(300 < length(to.post)){ + print(to.post) + stop("your animint has ", length(to.post), + " files but the Gist API will not serve more than 300 files,", + " so your animint will not be viewable on bl.ocks.org.", + " Try using https://pages.github.com/ to share your animint,", + " or the chunk_vars argument to reduce the number of tsv files", + " http://bit.ly/21scnod") + } + if(any(1024 * 1024 < all.file.info$size)){ + print(all.file.info[, "size", drop=FALSE]) + stop("your animint has files bigger than 1MB,", + " but the Gist API will truncate files bigger than 1MB,", + " so your animint will not be viewable on bl.ocks.org.", + " Try using https://pages.github.com/ to share your animint,", + " or the chunk_vars argument to combine some tsv files", + " http://bit.ly/21scnod") + } + gist <- gistr::gist_create(to.post, description = description, + browse = FALSE, ...) + if (browse) + browseURL(sprintf("http://bl.ocks.org/%s/raw/%s/", + gist$owner$login, gist$id)) + gist +} diff --git a/R/helperFunctions.R b/R/helperFunctions.R new file mode 100644 index 000000000..4105e9d40 --- /dev/null +++ b/R/helperFunctions.R @@ -0,0 +1,40 @@ +## General helper functions + + +#' Convert \code{pt} value to \code{lines} +#' +#' @param pt_value Value in \code{pt} to be converted to \code{lines} +#' @return Value in \code{lines} +#' @note Does NOT work if input is not in \code{pt}. Input is returned as is. +pt.to.lines <- function(pt_value){ + if(attributes(pt_value)$unit == "pt"){ + pt_value <- round(as.numeric(pt_value) * (0.25/5.5), digits = 2) + } + as.numeric(pt_value) +} + + +#' Check if character is an RGB hexadecimal color value +#' @param x character +#' @return True/False value +#' @export +is.rgb <- function(x){ + if(is.null(x)) { + TRUE + } else { + (grepl("#", x) & nchar(x)==7) + } +} + +#' Convert R colors to RGB hexadecimal color values +#' @param x character +#' @return hexadecimal color value or "transparent" if is.na +#' @export +toRGB <- function(x){ + is.transparent <- is.na(x) | x=="transparent" + rgb.mat <- col2rgb(x) + rgb.vec <- rgb(t(rgb.mat), maxColorValue=255) + named.vec <- ifelse(is.transparent, "transparent", rgb.vec) + not.named <- as.character(named.vec) + not.named +} diff --git a/R/knitr.R b/R/knitr.R new file mode 100644 index 000000000..52fdfa7bd --- /dev/null +++ b/R/knitr.R @@ -0,0 +1,143 @@ +#' Insert an interactive animation into an R markdown document using a customized print method. +#' @param x named list of ggplots and option lists to pass to \code{animint2dir}. +#' @param options knitr options. +#' @param ... placeholder. +#' @references https://github.com/yihui/knitr/blob/master/vignettes/knit_print.Rmd +#' @author Carson Sievert +#' @export +knit_print.animint2 <- function(x, options, ...) { + if (!requireNamespace("knitr")) warning("Please install.packages('knitr')") + # This function should be evaluated in knitr's output directory + output.dir <- knitr::opts_knit$get()[["output.dir"]] + ## sink() + ## print(output.dir) + ## browser() + old.wd <- setwd(output.dir) + on.exit(setwd(old.wd)) + # the current knitr chunk 'label' defines a directory to place the animints + # hopefully this regular expression is safe enough to workaround bad chunk names + # http://stackoverflow.com/questions/8959243/r-remove-non-alphanumeric-symbols-from-a-string + dir <- gsub("[^[:alnum:]]", "", options$label) + animint2dir(x, out.dir = dir, json.file = 'plot.json', open.browser = FALSE) + res <- new_animint(list(id = dir), json.file = file.path(dir, 'plot.json')) + # if this is the first plot, place scripts just before the plot + # there has to be a better way to do this, but this will do for now -- http://stackoverflow.com/questions/14308240/how-to-add-javascript-in-the-head-of-a-html-knitr-document + if (length(knitr::knit_meta(class = "animint", clean = FALSE)) == 0) { + res <- sprintf(' + + + + + +%s', dir, dir, dir, dir, dir, res) + } + knitr::asis_output(res, meta = list(animint = structure("", class = "animint"))) +} + +# Helper function to create the HTML needed to embed animint plots +# Note htmltools provides a better of doing this, but trying to avoid yet another dependency +new_animint <- function(attrs, json.file) { + jsonFile <- paste0('"', json.file, '"') + nms <- names(attrs) + attrz <- paste(nms, shQuote(attrs), sep = '=', collapse = ' ') + idx <- which(nms == 'id') + classx <- which(nms == 'class') + if (length(idx)) { + prefix <- '"#' + nm <- attrs[[idx]] + } else if (length(classx)) { + prefix <- '".' + nm <- attrs[[idx]] + } else warning('Unknown attribute') + # using chunk labels is problematic for JS variable names is problematic since '-', '.', etc are illegal + escaped <- gsub("[-.]", "_", nm) + selectr <- paste0(prefix, escaped) + paste0('

\n
\n') +} + +#' Shiny ui output function +#' @param outputId output variable to read the plot from +#' @seealso http://shiny.rstudio.com/articles/building-outputs.html +#' @export +#' +animintOutput <- function(outputId) { + # Note that requireNamespace("shiny") should load digest & htmltools (both used later on) + if (!requireNamespace("shiny")) message("Please install.packages('shiny')") + deps <- lapply(animint_dependencies(), shiny::createWebDependency) + htmltools::attachDependencies(htmltools::tags$div(id = outputId, class = 'shinyAnimint'), deps) +} + +#' Create an animint output element +#' +#' Shiny server output function customized for animint plots +#' (similar to \code{shiny::plotOutput} and friends). +#' +#' @param expr An expression that creates a list of ggplot objects. +#' @param env The environment in which to evaluate \code{expr}. +#' @param quoted Is expr a quoted expression (with \code{quote()})? +#' This is useful if you want to save an expression in a variable. +#' @seealso http://shiny.rstudio.com/articles/building-outputs.html +#' @export +#' +renderAnimint <- function(expr, env = parent.frame(), quoted = FALSE) { + # Note that requireNamespace("shiny") should load digest & htmltools (both used later on) + if (!requireNamespace("shiny")) message("Please install.packages('shiny')") + + # Convert the expression + environment into a function + func <- shiny::exprToFunction(expr, env, quoted) + + # this will tell knitr how to place animint into an interactive document + # implementation is similar to htmlwidgets::shinyRenderWidget + # we can't use that in our case since we must call animint2dir + # everytime shiny calls renderFunc + renderFunc <- function(shinysession, name, ...) { + val <- func() + tmp <- tempfile() + dir.create(tmp) + stuff <- animint2dir(val, out.dir = tmp, open.browser = FALSE) + shiny::addResourcePath("animintAssets", tmp) + list(jsonFile = "plot.json") + } + shiny::markRenderFunction(animint::animintOutput, renderFunc) +} + +# html dependencies according htmltools protocols +# these are here basically so we can take advantage of shiny::createWebDependency +animint_dependencies <- function() { + list(html_dependency_d3(), + html_dependency_animint(), + html_dependency_shinyAnimint()) +} + +html_dependency_d3 <- function() { + htmltools::htmlDependency(name = "d3", + version = "3.0.0", + src = system.file("htmljs/vendor", package = "animint"), + script = "d3.v3.js") +} + +html_dependency_animint <- function() { + htmltools::htmlDependency(name = "animint", + version = packageVersion("animint"), + src = system.file("htmljs", package = "animint"), + script = "animint.js") +} + +html_dependency_shinyAnimint <- function() { + htmltools::htmlDependency(name = "shinyAnimint", + version = packageVersion("animint"), + src = system.file("shiny", package = "animint"), + script = "shinyAnimint.js") +} + +html_dependency_plotJSON <- function(path, fileName) { + htmltools::htmlDependency(name = "plotJSON", + version = packageVersion("animint"), + src = path, + script = fileName) +} + diff --git a/R/print.R b/R/print.R new file mode 100644 index 000000000..a3b5635fc --- /dev/null +++ b/R/print.R @@ -0,0 +1,3 @@ +print.animint <- function(x, ...){ + animint2dir(x, ...) +} diff --git a/R/scales.R b/R/scales.R new file mode 100644 index 000000000..58e03ec14 --- /dev/null +++ b/R/scales.R @@ -0,0 +1,10 @@ +#' Scale point sizes using circle area, but specifying the radius in +#' pixels. +#' @param pixel.range min and max circle radius in pixels. +#' @param ... passed to continuous_scale. +#' @export +scale_size_animint <- function(pixel.range=c(2,20), ...){ + continuous_scale("size","area",palette=function(x){ + scales::rescale(sqrt(abs(x)), pixel.range, c(0,1)) + },...) +} diff --git a/R/testHelpers.R b/R/testHelpers.R new file mode 100644 index 000000000..3542144c7 --- /dev/null +++ b/R/testHelpers.R @@ -0,0 +1,166 @@ +#' Initiate external processes necessary for running tests. +#' +#' Initiates a local file server and remote driver. +#' +#' @param browserName Name of the browser to use for testing. +#' See ?RSelenium::remoteDriver for details. +#' @param dir character string with the path to animint's source code. Defaults to current directory +#' @param port port number used for local file server +#' @param ... list of additional options passed onto RSelenium::remoteDriver +#' @return invisible(TRUE) +#' @export +#' @seealso \link{tests_run} +#' +tests_init <- function(browserName = "phantomjs", dir = ".", port = 4848, ...) { + # try to exit out of previously initated processes + ex <- tests_exit() + # start a non-blocking local file server under path/to/animint/tests/testhat + testPath <- find_test_path(dir) + run_servr(port = port, directory = testPath) + # animint tests are performed in path/to/testthat/animint-htmltest/ + # note this path has to match the out.dir argument in animint2THML... + testDir <- file.path(testPath, "animint-htmltest") + # if the htmltest directory exists, wipe clean, then create an empty folder + unlink(testDir, recursive = TRUE) + dir.create(testDir) + # start-up remote driver + if (browserName == "phantomjs") { + message("Starting phantomjs binary. To shut it down, run: \n pJS$stop()") + pJS <<- RSelenium::phantom() + } else { + message("Starting selenium binary. To shut it down, run: \n", + "remDr$closeWindow() \n", + "remDr$closeServer()") + RSelenium::checkForServer(dir = system.file("bin", package = "RSelenium")) + selenium <- RSelenium::startServer() + } + # give an binaries a moment to start up + Sys.sleep(8) + remDr <<- RSelenium::remoteDriver(browserName = browserName, ...) + # give the backend a moment to start-up + Sys.sleep(6) + remDr$open(silent = TRUE) + Sys.sleep(2) + # some tests don't run reliably with phantomjs (see tests-widerect.R) + Sys.setenv("ANIMINT_BROWSER" = browserName) + # wait a maximum of 30 seconds when searching for elements. + remDr$setImplicitWaitTimeout(milliseconds = 30000) + # wait a maximum of 30 seconds for a particular type of operation to execute + remDr$setTimeout(type = "page load", milliseconds = 30000) + # if we navigate to localhost:%s/htmltest directly, some browsers will + # redirect to www.htmltest.com. A 'safer' approach is to navigate, then click. + remDr$navigate(sprintf("http://localhost:%s/animint-htmltest/", port)) + + ## Why not just navigate to the right URL to begin with? + + ## e <- remDr$findElement("xpath", "//a[@href='animint-htmltest/']") + ## e$clickElement() + + invisible(TRUE) +} + +#' Run animint tests +#' +#' Convenience function for running animint tests. +#' +#' @param dir character string with the path to animint's source code. Defaults to current directory +#' @param filter If not NULL, only tests with file names matching +#' this regular expression will be executed. Matching will take on the +#' file name after it has been stripped of "test-" and ".r". +#' @export +#' @examples +#' +#' \dontrun{ +#' # run tests in test-rotate.R with Firefox +#' tests_init("firefox") +#' tests_run(filter = "rotate") +#' # clean-up +#' tests_exit() +#' } +#' + +tests_run <- function(dir = ".", filter = NULL) { + if (!"package:RSelenium" %in% search()) + stop("Please load RSelenium: library(RSelenium)") + if (!"package:testthat" %in% search()) + stop("Please load testthat: library(testthat)") + testDir <- find_test_path(dir) + # testthat::test_check assumes we are in path/to/animint/tests + old <- getwd() + on.exit(setwd(old), add = TRUE) + setwd(dirname(testDir)) + # avoid weird errors if this function is called via testhat::check() + # https://github.com/hadley/testthat/issues/144 + Sys.setenv("R_TESTS" = "") + testthat::test_check("animint2", filter = filter) +} + +#' Kill child process(es) that may have been initiated in animint testing +#' +#' Read process IDs from a file and kill those process(es) +#' +#' @seealso \link{tests_run} +#' @export +tests_exit <- function() { + res <- stop_binary() + Sys.unsetenv("ANIMINT_BROWSER") + f <- file.path(find_test_path(), "pids.txt") + if (file.exists(f)) { + e <- try(readLines(con <- file(f), warn = FALSE), silent = TRUE) + if (!inherits(e, "try-error")) { + pids <- as.integer(e) + res <- c(res, tools::pskill(pids)) + } + close(con) + unlink(f) + } + invisible(all(res)) +} + +#' Spawn a child R session that runs a 'blocking' command +#' +#' Run a blocking command in a child R session (for example a file server or shiny app) +#' +#' @param directory path that the server should map to. +#' @param port port number to _attempt_ to run server on. +#' @param code R code to execute in a child session +#' @return port number of the successful attempt +run_servr <- function(directory = ".", port = 4848, + code = "servr::httd(dir='%s', port=%d)") { + dir <- normalizePath(directory, winslash = "/", mustWork = TRUE) + cmd <- sprintf( + paste("write.table(Sys.getpid(), file='%s', append=T, row.name=F, col.names=F);", code), + file.path(find_test_path(), "pids.txt"), dir, port + ) + system2("Rscript", c("-e", shQuote(cmd)), wait = FALSE) +} + +# -------------------------- +# Functions that are used in multiple places +# -------------------------- + +stop_binary <- function() { + if (exists("pJS")) pJS$stop() + # these methods are really queries to the server + # thus, if it is already shut down, we get some arcane error message + e <- try({ + remDr$closeWindow() + remDr$closeServer() + }, silent = TRUE) + TRUE +} + +# find the path to animint's testthat directory +find_test_path <- function(dir = ".") { + dir <- normalizePath(dir, winslash = "/", mustWork = TRUE) + if (!grepl("animint", dir, fixed = TRUE)) + stop("animint must appear somewhere in 'dir'") + base_dir <- basename(dir) + if (!base_dir %in% c("animint2", "tests", "testthat")) + stop("Basename of dir must be one of: 'animint2', 'tests', 'testhat'") + ext_dir <- switch(base_dir, + animint2 = "tests/testthat", + tests = "testthat", + testthat = "") + file.path(dir, ext_dir) +} diff --git a/R/theme.R b/R/theme.R new file mode 100644 index 000000000..a37ef8250 --- /dev/null +++ b/R/theme.R @@ -0,0 +1,26 @@ +##' theme for passing animint specific params +##' +##' Theme without checks. This allows us to write +##' \code{theme_animint(width=500)}, instead of \code{theme(animint.width=500)} +##' which gives an error in ggplot2 because users should be informed +##' if they mis-type standard theme element +##' names. https://github.com/hadley/ggplot2/issues/938 +##' @param ... theme options such as \code{width}. Use \code{update_axes=c("x", "y")} to update the axes of plots. Works for single selection variables. +##' @return ggplot theme list with names such as \code{animint.width}. +##' @examples +##' mtcars$cyl <- as.factor(mtcars$cyl) +##' p <- ggplot() + +##' geom_point(aes(x=wt, y=mpg, colour=cyl), +##' data=mtcars) + +##' ## set width and height values and update both axes +##' theme_animint(width=600, height=600, update_axes=c("x", "y")) +##' viz <- list(plot=p, selector.types=list(cyl="single")) +##' animint2dir(viz) +##' @export +##' @author Toby Dylan Hocking +theme_animint <- function(...){ + elements <- list(...) + names(elements) <- paste0("animint.", names(elements)) + elements$validate <- FALSE + do.call(theme, elements) +} diff --git a/R/transformShape.R b/R/transformShape.R new file mode 100644 index 000000000..d2cdc76e8 --- /dev/null +++ b/R/transformShape.R @@ -0,0 +1,35 @@ +#' Function to transform R shapes into d3 shapes... +#' @param dframe Data frame with columns shape, fill, colour. +#' @return Data frame transformed so that shape corresponds to d3 shape. Also includes Rshape column for debugging. +transform_shape <- function(dframe){ + dframe[,2:3] <- apply(dframe[,2:3], 2, as.character) + unfilled <- which(dframe$shape<=14) + solid <- which(dframe$shape>14 & dframe$shape<=20) + outlined <- which(dframe$shape>20) + xold <- dframe$shape + shapeidx <- data.frame(x = 0:25, shape="", stringsAsFactors=FALSE) + shapeidx[c(0, 7, 12, 13, 14, 15, 22)+1,2] <- "square" + shapeidx[c(3, 4, 8)+1, 2] <- "cross" + shapeidx[c(5, 9, 18, 23)+1, 2] <- "diamond" + shapeidx[c(1, 10, 16, 19, 20, 21)+1, 2] <- "circle" + shapeidx[c(6, 11, 25)+1, 2] <- "triangle-down" + shapeidx[c(2, 17, 24)+1, 2] <- "triangle-up" + shapeidx$fill <- c(rep(FALSE, 15), rep(TRUE, 6), rep(TRUE, 5)) + shapeidx$line <- c(rep(TRUE, 15), rep(FALSE, 6), rep(TRUE, 5)) + vals <- unique(xold) + + dframe$fill[unfilled] <- "null" + dframe$colour[solid] <- "null" + dframe$Rshape <- dframe$shape + dframe$shape <- shapeidx$shape[xold+1] + + + # 16, 19, 20 are duplicates + # 0, 7, 12, 13, 14 are duplicates + # 3, 4, 8 are duplicates + # 5, 9 are duplicates + # 1, 10 are duplicates + # 6, 11 are duplicates + # 4, 7, 8, 9, 10, 11, 12, 13, 14, 19, 20 are not equivalent to their R shapes + return(dframe) +} \ No newline at end of file diff --git a/data/ChromHMMiterations.RData b/data/ChromHMMiterations.RData new file mode 100644 index 000000000..af93d6669 Binary files /dev/null and b/data/ChromHMMiterations.RData differ diff --git a/data/FluView.RData b/data/FluView.RData new file mode 100644 index 000000000..964f5355f Binary files /dev/null and b/data/FluView.RData differ diff --git a/data/FunctionalPruning.RData b/data/FunctionalPruning.RData new file mode 100644 index 000000000..92dc859a3 Binary files /dev/null and b/data/FunctionalPruning.RData differ diff --git a/data/PeakConsistency.RData b/data/PeakConsistency.RData new file mode 100644 index 000000000..27a6bf040 Binary files /dev/null and b/data/PeakConsistency.RData differ diff --git a/data/TestROC.RData b/data/TestROC.RData new file mode 100644 index 000000000..a6ee73fa9 Binary files /dev/null and b/data/TestROC.RData differ diff --git a/data/UStornadoes.RData b/data/UStornadoes.RData new file mode 100644 index 000000000..350bbcffa Binary files /dev/null and b/data/UStornadoes.RData differ diff --git a/data/VariantModels.RData b/data/VariantModels.RData new file mode 100644 index 000000000..5162c7d31 Binary files /dev/null and b/data/VariantModels.RData differ diff --git a/data/WorldBank.RData b/data/WorldBank.RData new file mode 100644 index 000000000..64110cc84 Binary files /dev/null and b/data/WorldBank.RData differ diff --git a/data/breakpoints.RData b/data/breakpoints.RData new file mode 100644 index 000000000..d9733623f Binary files /dev/null and b/data/breakpoints.RData differ diff --git a/data/change.RData b/data/change.RData new file mode 100644 index 000000000..55ebc123c Binary files /dev/null and b/data/change.RData differ diff --git a/data/climate.RData b/data/climate.RData new file mode 100644 index 000000000..3806abd84 Binary files /dev/null and b/data/climate.RData differ diff --git a/data/compare.RData b/data/compare.RData new file mode 100644 index 000000000..99e83ad93 Binary files /dev/null and b/data/compare.RData differ diff --git a/data/generation.loci.RData b/data/generation.loci.RData new file mode 100644 index 000000000..bc660997c Binary files /dev/null and b/data/generation.loci.RData differ diff --git a/data/intreg.RData b/data/intreg.RData new file mode 100644 index 000000000..1b8a8e9fa Binary files /dev/null and b/data/intreg.RData differ diff --git a/data/malaria.RData b/data/malaria.RData new file mode 100644 index 000000000..ece469f44 Binary files /dev/null and b/data/malaria.RData differ diff --git a/data/mixtureKNN.RData b/data/mixtureKNN.RData new file mode 100644 index 000000000..3976a9cf8 Binary files /dev/null and b/data/mixtureKNN.RData differ diff --git a/data/pirates.RData b/data/pirates.RData new file mode 100644 index 000000000..c225be8e8 Binary files /dev/null and b/data/pirates.RData differ diff --git a/data/prior.RData b/data/prior.RData new file mode 100644 index 000000000..c12d20c48 Binary files /dev/null and b/data/prior.RData differ diff --git a/data/prostateLasso.RData b/data/prostateLasso.RData new file mode 100644 index 000000000..654dfc253 Binary files /dev/null and b/data/prostateLasso.RData differ diff --git a/data/vervet.RData b/data/vervet.RData new file mode 100644 index 000000000..38da244df Binary files /dev/null and b/data/vervet.RData differ diff --git a/data/worldPop.RData b/data/worldPop.RData new file mode 100644 index 000000000..578532c14 Binary files /dev/null and b/data/worldPop.RData differ diff --git a/inst/htmljs/animint.js b/inst/htmljs/animint.js new file mode 100644 index 000000000..49e709bc1 --- /dev/null +++ b/inst/htmljs/animint.js @@ -0,0 +1,2609 @@ +// Define functions to render linked interactive plots using d3. +// Another script should define e.g. +// +// Constructor for animint Object. +var animint = function (to_select, json_file) { + + function wait_until_then(timeout, condFun, readyFun) { + var args=arguments + function checkFun() { + if(condFun()) { + readyFun(args[3],args[4]); + } else{ + setTimeout(checkFun, timeout); + } + } + checkFun(); + } + + function convert_R_types(resp_array, types){ + return resp_array.map(function (d) { + for (var v_name in d) { + if(!is_interactive_aes(v_name)){ + var r_type = types[v_name]; + if (r_type == "integer") { + d[v_name] = parseInt(d[v_name]); + } else if (r_type == "numeric") { + d[v_name] = parseFloat(d[v_name]); + } else if (r_type == "factor" || r_type == "rgb" + || r_type == "linetype" || r_type == "label" + || r_type == "character") { + // keep it as a character + } else if (r_type == "character" & v_name == "outliers") { + d[v_name] = parseFloat(d[v_name].split(" @ ")); + } + } + } + return d; + }); + } + + // replacing periods in variable with an underscore this makes sure + // that selector doesn't confuse . in name with css selectors + function safe_name(unsafe_name){ + return unsafe_name.replace(/[ .]/g, '_'); + } + function legend_class_name(selector_name){ + return safe_name(selector_name) + "_variable"; + } + + function is_interactive_aes(v_name){ + if(v_name.indexOf("clickSelects") > -1){ + return true; + } + if(v_name.indexOf("showSelected") > -1){ + return true; + } + return false; + } + + var linetypesize2dasharray = function (lt, size) { + var isInt = function(n) { + return typeof n === 'number' && parseFloat(n) == parseInt(n, 10) && !isNaN(n); + }; + if(isInt(lt)){ // R integer line types. + if(lt == 1){ + return null; + } + var o = { + 0: size * 0 + "," + size * 10, + 2: size * 4 + "," + size * 4, + 3: size + "," + size * 2, + 4: size + "," + size * 2 + "," + size * 4 + "," + size * 2, + 5: size * 8 + "," + size * 4, + 6: size * 2 + "," + size * 2 + "," + size * 6 + "," + size * 2 + }; + } else { // R defined line types + if(lt == "solid" || lt === null){ + return null; + } + var o = { + "blank": size * 0 + "," + size * 10, + "none": size * 0 + "," + size * 10, + "dashed": size * 4 + "," + size * 4, + "dotted": size + "," + size * 2, + "dotdash": size + "," + size * 2 + "," + size * 4 + "," + size * 2, + "longdash": size * 8 + "," + size * 4, + "twodash": size * 2 + "," + size * 2 + "," + size * 6 + "," + size * 2, + "22": size * 2 + "," + size * 2, + "42": size * 4 + "," + size * 2, + "44": size * 4 + "," + size * 4,"13": size + "," + size * 3, + "1343": size + "," + size * 3 + "," + size * 4 + "," + size * 3, + "73": size * 7 + "," + size * 3, + "2262": size * 2 + "," + size * 2 + "," + size * 6 + "," + size * 2, + "12223242": size + "," + size * 2 + "," + size * 2 + "," + size * 2 + "," + size * 3 + "," + size * 2 + "," + size * 4 + "," + size * 2, + "F282": size * 15 + "," + size * 2 + "," + size * 8 + "," + size * 2, + "F4448444": size * 15 + "," + size * 4 + "," + size * 4 + "," + size * 4 + "," + size * 8 + "," + size * 4 + "," + size * 4 + "," + size * 4, + "224282F2": size * 2 + "," + size * 2 + "," + size * 4 + "," + size * 2 + "," + size * 8 + "," + size * 2 + "," + size * 16 + "," + size * 2, + "F1": size * 16 + "," + size + }; + } + + if (lt in o){ + return o[lt]; + } else{ // manually specified line types + str = lt.split(""); + strnum = str.map(function (d) { + return size * parseInt(d, 16); + }); + return strnum; + } + }; + + var isArray = function(o) { + return Object.prototype.toString.call(o) === '[object Array]'; + }; + + // create a dummy element, apply the appropriate classes, + // and then measure the element + // Inspired from http://jsfiddle.net/uzddx/2/ + var measureText = function(pText, pFontSize, pAngle, pStyle) { + if (!pText || pText.length === 0) return {height: 0, width: 0}; + if (pAngle === null || isNaN(pAngle)) pAngle = 0; + + var container = element.append('svg'); + // do we need to set the class so that styling is applied? + //.attr('class', classname); + + container.append('text') + .attr({x: -1000, y: -1000}) + .attr("transform", "rotate(" + pAngle + ")") + .attr("style", pStyle) + .attr("font-size", pFontSize) + .text(pText); + + var bbox = container.node().getBBox(); + container.remove(); + + return {height: bbox.height, width: bbox.width}; + }; + + var nest_by_group = d3.nest().key(function(d){ return d.group; }); + var dirs = json_file.split("/"); + dirs.pop(); //if a directory path exists, remove the JSON file from dirs + var element = d3.select(to_select); + this.element = element; + var viz_id = element.attr("id"); + var Widgets = {}; + this.Widgets = Widgets; + var Selectors = {}; + this.Selectors = Selectors; + var Plots = {}; + this.Plots = Plots; + var Geoms = {}; + this.Geoms = Geoms; + // SVGs must be stored separately from Geoms since they are + // initialized first, with the Plots. + var SVGs = {}; + this.SVGs = SVGs; + var Animation = {}; + this.Animation = Animation; + var all_geom_names = {}; + this.all_geom_names = all_geom_names; + + //creating an array to contain the selectize widgets + var selectized_array = []; + var data_object_geoms = { + "line":true, + "path":true, + "ribbon":true, + "polygon":true + }; + var css = document.createElement('style'); + css.type = 'text/css'; + var styles = [".axis path{fill: none;stroke: black;shape-rendering: crispEdges;}", + ".axis line{fill: none;stroke: black;shape-rendering: crispEdges;}", + ".axis text {font-family: sans-serif;font-size: 11px;}"]; + + var add_geom = function (g_name, g_info) { + // Determine what style to use to show the selection for this + // geom. This is a hack and should be removed when we implement + // the selected.color, selected.size, etc aesthetics. + if(g_info.aes.hasOwnProperty("fill") && + g_info.geom == "rect" && + g_info.aes.hasOwnProperty("clickSelects")){ + g_info.select_style = "stroke"; + }else{ + g_info.select_style = "opacity"; + } + // Determine if data will be an object or an array. + if(g_info.geom in data_object_geoms){ + g_info.data_is_object = true; + }else{ + g_info.data_is_object = false; + } + // Add a row to the loading table. + g_info.tr = Widgets["loading"].append("tr"); + g_info.tr.append("td").text(g_name); + g_info.tr.append("td").attr("class", "chunk"); + g_info.tr.append("td").attr("class", "downloaded").text(0); + g_info.tr.append("td").text(g_info.total); + g_info.tr.append("td").attr("class", "status").text("initialized"); + + // load chunk tsv + g_info.data = {}; + g_info.download_status = {}; + Geoms[g_name] = g_info; + // Determine whether common chunk tsv exists + // If yes, load it + if(g_info.hasOwnProperty("columns") && g_info.columns.common){ + var common_tsv = get_tsv(g_info, "_common"); + g_info.common_tsv = common_tsv; + var common_path = getTSVpath(common_tsv); + d3.tsv(common_path, function (error, response) { + var converted = convert_R_types(response, g_info.types); + g_info.data[common_tsv] = nest_by_group.map(converted); + }); + } else { + g_info.common_tsv = null; + } + // Save this geom and load it! + update_geom(g_name, null); + }; + var add_plot = function (p_name, p_info) { + // Each plot may have one or more legends. To make space for the + // legends, we put each plot in a table with one row and two + // columns: tdLeft and tdRight. + var plot_table = element.append("table").style("display", "inline-block"); + var plot_tr = plot_table.append("tr"); + var tdLeft = plot_tr.append("td"); + var tdRight = plot_tr.append("td").attr("class", p_name+"_legend"); + if(viz_id === null){ + p_info.plot_id = p_name; + }else{ + p_info.plot_id = viz_id + "_" + p_name; + } + var svg = tdLeft.append("svg") + .attr("id", p_info.plot_id) + .attr("height", p_info.options.height) + .attr("width", p_info.options.width); + + // divvy up width/height based on the panel layout + var nrows = Math.max.apply(null, p_info.layout.ROW); + var ncols = Math.max.apply(null, p_info.layout.COL); + var panel_names = p_info.layout.PANEL; + var npanels = Math.max.apply(null, panel_names); + + // Note axis names are "shared" across panels (just like the title) + var xtitlepadding = 5 + measureText(p_info["xtitle"], 11).height; + var ytitlepadding = 5 + measureText(p_info["ytitle"], 11).height; + + // 'margins' are fixed across panels and do not + // include title/axis/label padding (since these are not + // fixed across panels). They do, however, account for + // spacing between panels + var text_height_pixels = measureText("foo", 11).height; + var margin = { + left: 0, + right: text_height_pixels * p_info.panel_margin_lines, + top: text_height_pixels * p_info.panel_margin_lines, + bottom: 0 + }; + var plotdim = { + width: 0, + height: 0, + xstart: 0, + xend: 0, + ystart: 0, + yend: 0, + graph: { + width: 0, + height: 0 + }, + margin: margin, + xlab: { + x: 0, + y: 0 + }, + ylab: { + x: 0, + y: 0 + }, + title: { + x: 0, + y: 0 + } + }; + + // Draw the title + var titlepadding = measureText(p_info.title, 20).height + 10; + // why are we giving the title padding if it is undefined? + if (p_info.title === undefined) titlepadding = 0; + plotdim.title.x = p_info.options.width / 2; + plotdim.title.y = titlepadding / 2; + svg.append("text") + .text(p_info.title) + .attr("class", "plottitle") + .attr("font-family", "sans-serif") + .attr("font-size", "20px") + .attr("transform", "translate(" + plotdim.title.x + "," + + plotdim.title.y + ")") + .style("text-anchor", "middle"); + + // grab max text size over axis labels and facet strip labels + var axispaddingy = 5; + if(p_info.hasOwnProperty("ylabs") && p_info.ylabs.length){ + axispaddingy += Math.max.apply(null, p_info.ylabs.map(function(entry){ + // + 5 to give a little extra space to avoid bad axis labels + // in shiny. + return measureText(entry, 11).width + 5; + })); + } + var axispaddingx = 10 + 20; + if(p_info.hasOwnProperty("xlabs") && p_info.xlabs.length){ + // TODO: throw warning if text height is large portion of plot height? + axispaddingx += Math.max.apply(null, p_info.xlabs.map(function(entry){ + return measureText(entry, 11, p_info.xangle).height; + })); + // TODO: carefully calculating this gets complicated with rotating xlabs + //margin.right += 5; + } + plotdim.margin = margin; + + var strip_heights = p_info.strips.top.map(function(entry){ + return measureText(entry, 11).height; + }); + var strip_widths = p_info.strips.right.map(function(entry){ + return measureText(entry, 11).height; + }); + + // compute the number of x/y axes, max strip height per row, and + // max strip width per columns, for calculating height/width of + // graphing region. + var row_strip_heights = []; + var col_strip_widths = []; + var n_xaxes = 0; + var n_yaxes = 0; + var current_row, current_col; + for (var layout_i = 0; layout_i < npanels; layout_i++) { + current_row = p_info.layout.ROW[layout_i] - 1; + current_col = p_info.layout.COL[layout_i] - 1; + if(row_strip_heights[current_row] === undefined){ + row_strip_heights[current_row] = []; + } + if(col_strip_widths[current_col] === undefined){ + col_strip_widths[current_col] = []; + } + row_strip_heights[current_row].push(strip_heights[layout_i]); + col_strip_widths[current_col].push(strip_widths[layout_i]); + if (p_info.layout.COL[layout_i] == 1) { + n_xaxes += p_info.layout.AXIS_X[layout_i]; + } + if (p_info.layout.ROW[layout_i] == 1) { + n_yaxes += p_info.layout.AXIS_Y[layout_i]; + } + } + function cumsum_array(array_of_arrays){ + var cumsum = [], max_value, cumsum_value = 0; + for(var i=0; i 1) { + background.append("rect") + .attr("x", plotdim.xstart) + .attr("y", plotdim.ystart) + .attr("width", plotdim.xend - plotdim.xstart) + .attr("height", plotdim.yend - plotdim.ystart) + .attr("class", "background_rect") + .style("fill", p_info.panel_background.fill) + .style("stroke", p_info.panel_background.colour) + .style("stroke-dasharray", function() { + return linetypesize2dasharray(p_info.panel_background.linetype, + p_info.panel_background.size); + }); + } + + // function to draw major/minor grid lines + var grid_line = function(grid_background, grid_class) { + // if grid lines are defined + if(Object.keys(grid_background).length > 1) { + var col = grid_background.colour; + var lt = grid_background.linetype; + var size = grid_background.size; + var cap = grid_background.lineend; + // group for grid lines + var grid = background.append("g") + .attr("class", grid_class); + + // group for horizontal grid lines + var grid_hor = grid.append("g") + .attr("class", "hor"); + // draw horizontal grid lines if they are defined + if(typeof grid_background.loc.y != "undefined") { + // coercing y lines to array if necessary + if(typeof grid_background.loc.y == "number") grid_background.loc.y = [grid_background.loc.y]; + // drawing lines + grid_hor.selectAll("line") + .data(function() { return d3.values(grid_background.loc.y); }) + .enter() + .append("line") + .attr("x1", plotdim.xstart) + .attr("x2", plotdim.xend) + .attr("y1", function(d) { return scales[panel_i].y(d); }) + .attr("y2", function(d) { return scales[panel_i].y(d); }) + .style("stroke", col) + .style("stroke-linecap", cap) + .style("stroke-width", size) + .style("stroke-dasharray", function() { + return linetypesize2dasharray(lt, size); + });; + } + + // group for vertical grid lines + var grid_vert = grid.append("g") + .attr("class", "vert"); + // draw vertical grid lines if they are defined + if(typeof grid_background.loc.x != "undefined") { + // coercing x lines to array if necessary + if(typeof grid_background.loc.x == "number") grid_background.loc.x = [grid_background.loc.x]; + // drawing lines + grid_vert.selectAll("line") + .data(function() { return d3.values(grid_background.loc.x); }) + .enter() + .append("line") + .attr("x1", function(d) { return scales[panel_i].x(d); }) + .attr("x2", function(d) { return scales[panel_i].x(d); }) + .attr("y1", plotdim.ystart) + .attr("y2", plotdim.yend) + .style("stroke", col) + .style("stroke-linecap", cap) + .style("stroke-width", size) + .style("stroke-dasharray", function() { + return linetypesize2dasharray(lt, size); + });; + } + } + } + // drawing the grid lines + grid_line(p_info.grid_minor, "grid_minor"); + grid_line(p_info.grid_major, "grid_major"); + + // drawing border + // uses insert to draw it right before the #plottitle + if(Object.keys(p_info.panel_border).length > 1) { + background.append("rect") + .attr("x", plotdim.xstart) + .attr("y", plotdim.ystart) + .attr("width", plotdim.xend - plotdim.xstart) + .attr("height", plotdim.yend - plotdim.ystart) + .attr("class", "border_rect") + .style("fill", p_info.panel_border.fill) + .style("stroke", p_info.panel_border.colour) + .style("stroke-dasharray", function() { + return linetypesize2dasharray(p_info.panel_border.linetype, + p_info.panel_border.size); + }); + } + + } //end of for(layout_i + // After drawing all backgrounds, we can draw the axis labels. + if(p_info["ytitle"]){ + svg.append("text") + .text(p_info["ytitle"]) + .attr("class", "ytitle") + .style("text-anchor", "middle") + .style("font-size", "11px") + .attr("transform", "translate(" + + ytitle_x + + "," + + (ytitle_top + ytitle_bottom)/2 + + ")rotate(270)") + ; + } + if(p_info["xtitle"]){ + svg.append("text") + .text(p_info["xtitle"]) + .attr("class", "xtitle") + .style("text-anchor", "middle") + .style("font-size", "11px") + .attr("transform", "translate(" + + (xtitle_left + xtitle_right)/2 + + "," + + xtitle_y + + ")") + ; + } + Plots[p_name].scales = scales; + }; //end of add_plot() + + function update_legend_opacity(v_name){ + var s_info = Selectors[v_name]; + s_info.legend_tds.style("opacity", s_info.legend_update_fun); + } + + var add_selector = function (s_name, s_info) { + Selectors[s_name] = s_info; + if(s_info.type == "multiple"){ + if(!isArray(s_info.selected)){ + s_info.selected = [s_info.selected]; + } + // legend_update_fun is evaluated in the context of the + // td.legend_entry_label. + s_info.legend_update_fun = function(d){ + var i_value = s_info.selected.indexOf(this.textContent); + if(i_value == -1){ + return 0.5; + }else{ + return 1; + } + } + }else{ + s_info.legend_update_fun = function(d){ + if(this.textContent == s_info.selected){ + return 1; + }else{ + return 0.5; + } + } + } + s_info.legend_tds = + element.selectAll("tr."+legend_class_name(s_name)+" td.legend_entry_label") + ; + update_legend_opacity(s_name); + }; //end of add_selector() + + function get_tsv(g_info, chunk_id){ + return g_info.classed + "_chunk" + chunk_id + ".tsv"; + } + function getTSVpath(tsv_name){ + return dirs.concat(tsv_name).join("/"); + } + + /** + * copy common chunk tsv to varied chunk tsv, returning an array of + * objects. + */ + function copy_chunk(g_info, varied_chunk) { + var varied_by_group = nest_by_group.map(varied_chunk); + var common_by_group = g_info.data[g_info.common_tsv]; + var new_varied_chunk = []; + for(group_id in varied_by_group){ + var varied_one_group = varied_by_group[group_id]; + var common_one_group = common_by_group[group_id]; + var common_i = 0; + for(var varied_i=0; varied_i < varied_one_group.length; varied_i++){ + // there are two cases: each group of varied data is of length + // 1, or of length of the common data. + if(common_one_group.length == varied_one_group.length){ + common_i = varied_i; + } + var varied_obj = varied_one_group[varied_i]; + var common_obj = common_one_group[common_i]; + for(col in common_obj){ + if(col != "group"){ + varied_obj[col] = common_obj[col]; + } + } + new_varied_chunk.push(varied_obj); + } + } + return new_varied_chunk; + } + + // update_geom is called from add_geom and update_selector. It + // downloads data if necessary, and then calls draw_geom. + var update_geom = function (g_name, selector_name) { + var g_info = Geoms[g_name]; + // First apply chunk_order selector variables. + var chunk_id = g_info.chunks; + g_info.chunk_order.forEach(function (v_name) { + if(chunk_id == null){ + return; // no data in a higher up chunk var. + } + var value = Selectors[v_name].selected; + if(chunk_id.hasOwnProperty(value)){ + chunk_id = chunk_id[value]; + }else{ + chunk_id = null; // no data to show in this subset. + } + }); + if(chunk_id == null){ + draw_panels(g_info, [], selector_name); //draw nothing. + return; + } + var tsv_name = get_tsv(g_info, chunk_id); + // get the data if it has not yet been downloaded. + g_info.tr.select("td.chunk").text(tsv_name); + if(g_info.data.hasOwnProperty(tsv_name)){ + draw_panels(g_info, g_info.data[tsv_name], selector_name); + }else{ + g_info.tr.select("td.status").text("downloading"); + var svg = SVGs[g_name]; + var loading = svg.append("text") + .attr("class", "loading"+tsv_name) + .text("Downloading "+tsv_name+"...") + .attr("font-size", 9) + //.attr("x", svg.attr("width")/2) + .attr("y", 10) + .style("fill", "red"); + download_chunk(g_info, tsv_name, function(chunk){ + loading.remove(); + draw_panels(g_info, chunk, selector_name); + }); + } + }; + var draw_panels = function(g_info, chunk, selector_name) { + // derive the plot name from the geometry name + var g_names = g_info.classed.split("_"); + var p_name = g_names[g_names.length - 1]; + var panels = Plots[p_name].layout.PANEL; + panels.forEach(function(panel) { + draw_geom(g_info, chunk, selector_name, panel); + }); + }; + + function download_next(g_name){ + var g_info = Geoms[g_name]; + var selector_value = Animation.sequence[g_info.seq_i]; + var chunk_id = g_info.chunks[selector_value]; + var tsv_name = get_tsv(g_info, chunk_id); + g_info.seq_count += 1; + if(Animation.sequence.length == g_info.seq_count){ + Animation.done_geoms[g_name] = 1; + return; + } + g_info.seq_i += 1; + if(g_info.seq_i == Animation.sequence.length){ + g_info.seq_i = 0; + } + if(typeof(chunk_id) == "string"){ + download_chunk(g_info, tsv_name, function(chunk){ + download_next(g_name); + }) + }else{ + download_next(g_name); + } + } + + // download_chunk is called from update_geom and download_next. + function download_chunk(g_info, tsv_name, funAfter){ + if(g_info.download_status.hasOwnProperty(tsv_name)){ + funAfter(); + return; // do not download twice. + } + g_info.download_status[tsv_name] = "downloading"; + // prefix tsv file with appropriate path + var tsv_file = getTSVpath(tsv_name); + d3.tsv(tsv_file, function (error, response) { + // First convert to correct types. + g_info.download_status[tsv_name] = "processing"; + response = convert_R_types(response, g_info.types); + wait_until_then(500, function(){ + if(g_info.common_tsv) { + return g_info.data.hasOwnProperty(g_info.common_tsv); + }else{ + return true; + } + }, function(){ + if(g_info.common_tsv) { + // copy data from common tsv to varied tsv + response = copy_chunk(g_info, response); + } + var nest = d3.nest(); + g_info.nest_order.forEach(function (v_name) { + nest.key(function (d) { + return d[v_name]; + }); + }); + var chunk = nest.map(response); + g_info.data[tsv_name] = chunk; + g_info.tr.select("td.downloaded").text(d3.keys(g_info.data).length); + g_info.download_status[tsv_name] = "saved"; + funAfter(chunk); + }); + }); + }//download_chunk. + + // update_geom is responsible for obtaining a chunk of downloaded + // data, and then calling draw_geom to actually draw it. + var draw_geom = function(g_info, chunk, selector_name, PANEL){ + g_info.tr.select("td.status").text("displayed"); + var svg = SVGs[g_info.classed]; + // derive the plot name from the geometry name + var g_names = g_info.classed.split("_"); + var p_name = g_names[g_names.length - 1]; + var scales = Plots[p_name].scales[PANEL]; + var selected_arrays = [ [] ]; //double array necessary. + g_info.subset_order.forEach(function (aes_name) { + var selected, values; + var new_arrays = []; + if(0 < aes_name.indexOf(".variable")){ + selected_arrays.forEach(function(old_array){ + var some_data = chunk; + old_array.forEach(function(value){ + if(some_data.hasOwnProperty(value)) { + some_data = some_data[value]; + } else { + some_data = {}; + } + }) + values = d3.keys(some_data); + values.forEach(function(s_name){ + var selected = Selectors[s_name].selected; + var new_array = old_array.concat(s_name).concat(selected); + new_arrays.push(new_array); + }) + }) + }else{//not .variable aes: + if(aes_name == "PANEL"){ + selected = PANEL; + }else{ + var s_name = g_info.aes[aes_name]; + selected = Selectors[s_name].selected; + } + if(isArray(selected)){ + values = selected; //multiple selection. + }else{ + values = [selected]; //single selection. + } + values.forEach(function(value){ + selected_arrays.forEach(function(old_array){ + var new_array = old_array.concat(value); + new_arrays.push(new_array); + }) + }) + } + selected_arrays = new_arrays; + }); + // data can be either an array[] if it will be directly involved + // in a data-bind, or an object{} if it will be involved in a + // data-bind by group (e.g. geom_line). + var data; + if(g_info.data_is_object){ + data = {}; + }else{ + data = []; + } + selected_arrays.forEach(function(value_array){ + var some_data = chunk; + value_array.forEach(function(value){ + if (some_data.hasOwnProperty(value)) { + some_data = some_data[value]; + } else { + if(g_info.data_is_object){ + some_data = {}; + }else{ + some_data = []; + } + } + }); + if(g_info.data_is_object){ + if(isArray(some_data) && some_data.length){ + data["0"] = some_data; + }else{ + for(k in some_data){ + data[k] = some_data[k]; + } + } + }else{//some_data is an array. + data = data.concat(some_data); + } + }); + var aes = g_info.aes; + var toXY = function (xy, a) { + return function (d) { + return scales[xy](d[a]); + }; + }; + var layer_g_element = svg.select("g." + g_info.classed); + var panel_g_element = layer_g_element.select("g.PANEL" + PANEL); + var elements = panel_g_element.selectAll(".geom"); + // TODO: standardize this code across aes/styles. + var base_opacity = 1; + if (g_info.params.alpha) { + base_opacity = g_info.params.alpha; + } + //alert(g_info.classed+" "+base_opacity); + var get_alpha = function (d) { + var a; + if (aes.hasOwnProperty("alpha") && d.hasOwnProperty("alpha")) { + a = d["alpha"]; + } else { + a = base_opacity; + } + return a; + }; + var size = 2; + if(g_info.geom == "text"){ + size = 12; + } + if (g_info.params.hasOwnProperty("size")) { + size = g_info.params.size; + } + var get_size = function (d) { + if (aes.hasOwnProperty("size") && d.hasOwnProperty("size")) { + return d["size"]; + } + return size; + }; + + // stroke_width for geom_point + var stroke_width = 1; // by default ggplot2 has 0.5, animint has 1 + if (g_info.params.hasOwnProperty("stroke")) { + stroke_width = g_info.params.stroke; + } + var get_stroke_width = function (d) { + if (aes.hasOwnProperty("stroke") && d.hasOwnProperty("stroke")) { + return d["stroke"]; + } + return stroke_width; + } + + var linetype = "solid"; + if (g_info.params.linetype) { + linetype = g_info.params.linetype; + } + + var get_dasharray = function (d) { + var lt = linetype; + if (aes.hasOwnProperty("linetype") && d.hasOwnProperty("linetype")) { + lt = d["linetype"]; + } + return linetypesize2dasharray(lt, get_size(d)); + }; + var colour = "black"; + var fill = "black"; + var get_colour = function (d) { + if (d.hasOwnProperty("colour")) { + return d["colour"] + } + return colour; + }; + var get_fill = function (d) { + if (d.hasOwnProperty("fill")) { + return d["fill"]; + } + return fill; + }; + if (g_info.params.colour) { + colour = g_info.params.colour; + } + if (g_info.params.fill) { + fill = g_info.params.fill; + }else if(g_info.params.colour){ + fill = g_info.params.colour; + } + + // For aes(hjust) the compiler should make an "anchor" column. + var text_anchor = "middle"; + if(g_info.params.hasOwnProperty("anchor")){ + text_anchor = g_info.params["anchor"]; + } + var get_text_anchor; + if(g_info.aes.hasOwnProperty("hjust")) { + get_text_anchor = function(d){ + return d["anchor"]; + } + }else{ + get_text_anchor = function(d){ + return text_anchor; + } + } + + var eActions, eAppend, linkActions; + var key_fun = null; + var id_fun = function(d){ + return d.id; + }; + if(g_info.aes.hasOwnProperty("key")){ + key_fun = function(d){ + return d.key; + }; + } + if(g_info.data_is_object) { + + // Lines, paths, polygons, and ribbons are a bit special. For + // every unique value of the group variable, we take the + // corresponding data rows and make 1 path. The tricky part is + // that to use d3 I do a data-bind of some "fake" data which are + // just group ids, which is the kv variable in the code below + + // // case of only 1 line and no groups. + // if(!aes.hasOwnProperty("group")){ + // kv = [{"key":0,"value":0}]; + // data = {0:data}; + // }else{ + // // we need to use a path for each group. + // var kv = d3.entries(d3.keys(data)); + // kv = kv.map(function(d){ + // d[aes.group] = d.value; + // return d; + // }); + // } + + // For an example consider breakpointError$error which is + // defined using this R code + + // geom_line(aes(segments, error, group=bases.per.probe, + // clickSelects=bases.per.probe), data=only.error, lwd=4) + + // Inside update_geom the variables take the following values + // (pseudo-Javascript code) + + // var kv = [{"key":"0","value":"133","bases.per.probe":"133"}, + // {"key":"1","value":"2667","bases.per.probe":"2667"}]; + // var data = {"133":[array of 20 points used to draw the line for group 133], + // "2667":[array of 20 points used to draw the line for group 2667]}; + + // I do elements.data(kv) so that when I set the d attribute of + // each path, I need to select the correct group before + // returning anything. + + // e.attr("d",function(group_info){ + // var one_group = data[group_info.value]; + // return lineThing(one_group); + // }) + + // To make color work I think you just have to select the group + // and take the color of the first element, e.g. + + // .style("stroke",function(group_info){ + // var one_group = data[group_info.value]; + // var one_row = one_group[0]; + // return get_color(one_row); + // } + + // In order to get d3 lines to play nice, bind fake "data" (group + // id's) -- the kv variable. Then each separate object is plotted + // using path (case of only 1 thing and no groups). + + // we need to use a path for each group. + var keyed_data = {}, one_group, group_id, k; + for(group_id in data){ + one_group = data[group_id]; + one_row = one_group[0]; + if(one_row.hasOwnProperty("key")){ + k = one_row.key; + }else{ + k = group_id; + } + keyed_data[k] = one_group; + } + var kv_array = d3.entries(d3.keys(keyed_data)); + var kv = kv_array.map(function (d) { + //d[aes.group] = d.value; + + // Need to store the clickSelects value that will + // be passed to the selector when we click on this + // item. + d.clickSelects = keyed_data[d.value][0].clickSelects; + return d; + }); + + // line, path, and polygon use d3.svg.line(), + // ribbon uses d3.svg.area() + // we have to define lineThing accordingly. + if (g_info.geom == "ribbon") { + var lineThing = d3.svg.area() + .x(toXY("x", "x")) + .y(toXY("y", "ymax")) + .y0(toXY("y", "ymin")); + } else { + var lineThing = d3.svg.line() + .x(toXY("x", "x")) + .y(toXY("y", "y")); + } + // select the correct group before returning anything. + key_fun = function(group_info){ + return group_info.value; + }; + id_fun = function(group_info){ + var one_group = keyed_data[group_info.value]; + var one_row = one_group[0]; + // take key from first value in the group. + return one_row.id; + }; + elements = elements.data(kv, key_fun); + linkActions = function(a_elements){ + a_elements + .attr("xlink:href", function(group_info){ + var one_group = keyed_data[group_info.value]; + var one_row = one_group[0]; + return one_row.href; + }) + .attr("target", "_blank") + .attr("class", "geom") + ; + }; + eActions = function (e) { + e.attr("d", function (d) { + var one_group = keyed_data[d.value]; + // filter NaN since they make the whole line disappear! + var no_na = one_group.filter(function(d){ + if(g_info.geom == "ribbon"){ + return !isNaN(d.x) && !isNaN(d.ymin) && !isNaN(d.ymax); + }else{ + return !isNaN(d.x) && !isNaN(d.y); + } + }); + return lineThing(no_na); + }) + .style("fill", function (group_info) { + if (g_info.geom == "line" || g_info.geom == "path") { + return "none"; + } + var one_group = keyed_data[group_info.value]; + var one_row = one_group[0]; + // take color for first value in the group + return get_fill(one_row); + }) + .style("stroke-width", function (group_info) { + var one_group = keyed_data[group_info.value]; + var one_row = one_group[0]; + // take size for first value in the group + return get_size(one_row); + }) + .style("stroke", function (group_info) { + var one_group = keyed_data[group_info.value]; + var one_row = one_group[0]; + // take color for first value in the group + return get_colour(one_row); + }) + .style("stroke-dasharray", function (group_info) { + var one_group = keyed_data[group_info.value]; + var one_row = one_group[0]; + // take linetype for first value in the group + return get_dasharray(one_row); + }) + .style("stroke-width", function (group_info) { + var one_group = keyed_data[group_info.value]; + var one_row = one_group[0]; + // take line size for first value in the group + return get_size(one_row); + }); + }; + eAppend = "path"; + }else{ + linkActions = function(a_elements){ + a_elements.attr("xlink:href", function(d){ return d.href; }) + .attr("target", "_blank") + .attr("class", "geom"); + }; + } + if (g_info.geom == "segment") { + elements = elements.data(data, key_fun); + eActions = function (e) { + e.attr("x1", function (d) { + return scales.x(d["x"]); + }) + .attr("x2", function (d) { + return scales.x(d["xend"]); + }) + .attr("y1", function (d) { + return scales.y(d["y"]); + }) + .attr("y2", function (d) { + return scales.y(d["yend"]); + }) + .style("stroke-dasharray", get_dasharray) + .style("stroke-width", get_size) + .style("stroke", get_colour); + }; + eAppend = "line"; + } + if (g_info.geom == "linerange") { + elements = elements.data(data, key_fun); + eActions = function (e) { + e.attr("x1", function (d) { + return scales.x(d["x"]); + }) + .attr("x2", function (d) { + return scales.x(d["x"]); + }) + .attr("y1", function (d) { + return scales.y(d["ymax"]); + }) + .attr("y2", function (d) { + return scales.y(d["ymin"]); + }) + .style("stroke-dasharray", get_dasharray) + .style("stroke-width", get_size) + .style("stroke", get_colour); + }; + eAppend = "line"; + } + if (g_info.geom == "vline") { + elements = elements.data(data, key_fun); + eActions = function (e) { + e.attr("x1", toXY("x", "xintercept")) + .attr("x2", toXY("x", "xintercept")) + .attr("y1", scales.y.range()[0]) + .attr("y2", scales.y.range()[1]) + .style("stroke-dasharray", get_dasharray) + .style("stroke-width", get_size) + .style("stroke", get_colour); + }; + eAppend = "line"; + } + if (g_info.geom == "hline") { + // pretty much a copy of geom_vline with obvious modifications + elements = elements.data(data, key_fun); + eActions = function (e) { + e.attr("y1", toXY("y", "yintercept")) + .attr("y2", toXY("y", "yintercept")) + .attr("x1", scales.x.range()[0]) + .attr("x2", scales.x.range()[1]) + .style("stroke-dasharray", get_dasharray) + .style("stroke-width", get_size) + .style("stroke", get_colour); + }; + eAppend = "line"; + } + if (g_info.geom == "text") { + elements = elements.data(data, key_fun); + // TODO: how to support vjust? firefox doensn't support + // baseline-shift... use paths? + // http://commons.oreilly.com/wiki/index.php/SVG_Essentials/Text + eActions = function (e) { + e.attr("x", toXY("x", "x")) + .attr("y", toXY("y", "y")) + .style("fill", get_colour) + .attr("font-size", get_size) + .style("text-anchor", get_text_anchor) + .text(function (d) { + return d.label; + }); + }; + eAppend = "text"; + } + if (g_info.geom == "point") { + elements = elements.data(data, key_fun); + eActions = function (e) { + e.attr("cx", toXY("x", "x")) + .attr("cy", toXY("y", "y")) + .attr("r", get_size) + .style("fill", get_fill) + .style("stroke", get_colour) + .style("stroke-width", get_stroke_width); + }; + eAppend = "circle"; + } + if (g_info.geom == "tallrect") { + elements = elements.data(data, key_fun); + eActions = function (e) { + e.attr("x", toXY("x", "xmin")) + .attr("width", function (d) { + return scales.x(d["xmax"]) - scales.x(d["xmin"]); + }) + .attr("y", scales.y.range()[1]) + .attr("height", scales.y.range()[0] - scales.y.range()[1]) + .style("fill", get_fill) + .style("stroke-dasharray", get_dasharray) + .style("stroke-width", get_size) + .style("stroke", get_colour); + }; + eAppend = "rect"; + } + if (g_info.geom == "widerect") { + elements = elements.data(data, key_fun); + eActions = function (e) { + e.attr("y", toXY("y", "ymax")) + .attr("height", function (d) { + return scales.y(d["ymin"]) - scales.y(d["ymax"]); + }) + .attr("x", scales.x.range()[0]) + .attr("width", scales.x.range()[1] - scales.x.range()[0]) + .style("fill", get_fill) + .style("stroke-dasharray", get_dasharray) + .style("stroke-width", get_size) + .style("stroke", get_colour); + }; + eAppend = "rect"; + } + if (g_info.geom == "rect") { + elements = elements.data(data, key_fun); + eActions = function (e) { + e.attr("x", toXY("x", "xmin")) + .attr("width", function (d) { + return Math.abs(scales.x(d.xmax) - scales.x(d.xmin)); + }) + .attr("y", toXY("y", "ymax")) + .attr("height", function (d) { + return Math.abs(scales.y(d.ymin) - scales.y(d.ymax)); + }) + .style("stroke-dasharray", get_dasharray) + .style("stroke-width", get_size) + .style("fill", get_fill); + if(g_info.select_style != "stroke"){ + e.style("stroke", get_colour); + } + }; + eAppend = "rect"; + } + if (g_info.geom == "boxplot") { + + // TODO: currently boxplots are unsupported (we intentionally + // stop with an error in the R code). The reason why is that + // boxplots are drawn using multiple geoms and it is not + // straightforward to deal with that using our current JS + // code. After all, a boxplot could be produced by combing 3 + // other geoms (rects, lines, and points) if you really wanted + // it. + + fill = "white"; + + elements = elements.data(data); + eActions = function (e) { + e.append("line") + .attr("x1", function (d) { + return scales.x(d["x"]); + }) + .attr("x2", function (d) { + return scales.x(d["x"]); + }) + .attr("y1", function (d) { + return scales.y(d["ymin"]); + }) + .attr("y2", function (d) { + return scales.y(d["lower"]); + }) + .style("stroke-dasharray", get_dasharray) + .style("stroke-width", get_size) + .style("stroke", get_colour); + e.append("line") + .attr("x1", function (d) { + return scales.x(d["x"]); + }) + .attr("x2", function (d) { + return scales.x(d["x"]); + }) + .attr("y1", function (d) { + return scales.y(d["upper"]); + }) + .attr("y2", function (d) { + return scales.y(d["ymax"]); + }) + .style("stroke-dasharray", get_dasharray) + .style("stroke-width", get_size) + .style("stroke", get_colour); + e.append("rect") + .attr("x", function (d) { + return scales.x(d["xmin"]); + }) + .attr("width", function (d) { + return scales.x(d["xmax"]) - scales.x(d["xmin"]); + }) + .attr("y", function (d) { + return scales.y(d["upper"]); + }) + .attr("height", function (d) { + return Math.abs(scales.y(d["upper"]) - scales.y(d["lower"])); + }) + .style("stroke-dasharray", get_dasharray) + .style("stroke-width", get_size) + .style("stroke", get_colour) + .style("fill", get_fill); + e.append("line") + .attr("x1", function (d) { + return scales.x(d["xmin"]); + }) + .attr("x2", function (d) { + return scales.x(d["xmax"]); + }) + .attr("y1", function (d) { + return scales.y(d["middle"]); + }) + .attr("y2", function (d) { + return scales.y(d["middle"]); + }) + .style("stroke-dasharray", get_dasharray) + .style("stroke-width", get_size) + .style("stroke", get_colour); + }; + } + elements.exit().remove(); + var enter = elements.enter(); + if(g_info.aes.hasOwnProperty("href")){ + enter = enter.append("svg:a") + .append("svg:"+eAppend); + }else{ + enter = enter.append(eAppend) + .attr("class", "geom"); + } + var has_clickSelects = g_info.aes.hasOwnProperty("clickSelects"); + var has_clickSelects_variable = + g_info.aes.hasOwnProperty("clickSelects.variable"); + if (has_clickSelects || has_clickSelects_variable) { + var selected_funs = { + "opacity":{ + "mouseout":function (d) { + var alpha_on = get_alpha(d); + var alpha_off = get_alpha(d) - 0.5; + if(has_clickSelects){ + return ifSelectedElse(d.clickSelects, g_info.aes.clickSelects, + alpha_on, alpha_off); + }else if(has_clickSelects_variable){ + return ifSelectedElse(d["clickSelects.value"], + d["clickSelects.variable"], + alpha_on, alpha_off); + } + }, + "mouseover":function (d) { + return get_alpha(d); + } + }, + "stroke":{ + "mouseout":function(d){ + var stroke_on = "black"; + var stroke_off = "transparent"; + if(has_clickSelects){ + return ifSelectedElse(d.clickSelects, g_info.aes.clickSelects, + stroke_on, stroke_off); + }else{ + return ifSelectedElse(d["clickSelects.value"], + d["clickSelects.variable"], + stroke_on, stroke_off); + } + }, + "mouseover":function(d){ + return "black"; + } + } + }; //selected_funs. + // My original design for clicking/interactivity/transparency: + // Basically I wanted a really simple way to show which element + // in a group of clickable geom elements is currently + // selected. So I decided that all the non-selected elements + // should have alpha transparency 0.5 less than normal, and the + // selected element should have normal alpha transparency. Also, + // the element currently under the mouse has normal alpha + // transparency, to visually indicate that it can be + // clicked. Looking at my examples, you will see that I + // basically use this in two ways: + + // 1. By specifying + // geom_vline(aes(clickSelects=variable),alpha=0.5), which + // implies a normal alpha transparency of 0.5. So all the vlines + // are hidden (normal alpha 0.5 - 0.5 = 0), except the current + // selection and the current element under the mouse pointer are + // drawn a bit faded with alpha=0.5. + + // 2. By specifying e.g. geom_point(aes(clickSelects=variable)), + // that implies a normal alpha=1. Thus the current selection and + // the current element under the mouse pointer are fully drawn + // with alpha=1 and the others are shown but a bit faded with + // alpha=0.5 (normal alpha 1 - 0.5 = 0.5). + + // Edit 19 March 2014: Now there are two styles to show the + // selection, depending on the geom. For most geoms it is as + // described above. But for geoms like rects with + // aes(fill=numericVariable), using opacity to indicate the + // selection results in a misleading decoding of the fill + // variable. So in this case we set stroke to "black" for the + // current selection. + + // TODO: user-configurable selection styles. + + var style_funs = selected_funs[g_info.select_style]; + var over_fun = function(e){ + e.style(g_info.select_style, style_funs["mouseover"]); + }; + var out_fun = function(e){ + e.style(g_info.select_style, style_funs["mouseout"]); + }; + elements.call(out_fun) + .on("mouseover", function (d) { + d3.select(this).call(over_fun); + }) + .on("mouseout", function (d) { + d3.select(this).call(out_fun); + }) + ; + if(has_clickSelects){ + elements.on("click", function (d) { + // The main idea of how clickSelects works: when we click + // something, we call update_selector with the clicked + // value. + var s_name = g_info.aes.clickSelects; + update_selector(s_name, d.clickSelects); + }); + }else{ + elements.on("click", function(d){ + var s_name = d["clickSelects.variable"]; + var s_value = d["clickSelects.value"]; + update_selector(s_name, s_value); + }); + } + }else{//has neither clickSelects nor clickSelects.variable. + elements.style("opacity", get_alpha); + } + var has_tooltip = g_info.aes.hasOwnProperty("tooltip"); + if(has_clickSelects || has_tooltip || has_clickSelects_variable){ + var text_fun, get_one; + if(g_info.data_is_object){ + get_one = function(d_or_kv){ + var one_group = keyed_data[d_or_kv.value]; + return one_group[0]; + }; + }else{ + get_one = function(d_or_kv){ + return d_or_kv; + }; + } + if(has_tooltip){ + text_fun = function(d){ + return d.tooltip; + }; + }else if(has_clickSelects){ + text_fun = function(d){ + var v_name = g_info.aes.clickSelects; + return v_name + " " + d.clickSelects; + }; + }else{ //clickSelects_variable + text_fun = function(d){ + return d["clickSelects.variable"] + " " + d["clickSelects.value"]; + }; + } + // if elements have an existing title, remove it. + elements.selectAll("title").remove(); + elements.append("svg:title") + .text(function(d_or_kv){ + var d = get_one(d_or_kv); + return text_fun(d); + }) + ; + } + // Set attributes of only the entering elements. This is needed to + // prevent things from flying around from the upper left when they + // enter the plot. + eActions(enter); // DO NOT DELETE! + if(Selectors.hasOwnProperty(selector_name)){ + var milliseconds = Selectors[selector_name].duration; + elements = elements.transition().duration(milliseconds); + } + if(g_info.aes.hasOwnProperty("id")){ + elements.attr("id", id_fun); + } + if(g_info.aes.hasOwnProperty("href")){ + // elements are , children are e.g. + var linked_geoms = elements.select(eAppend); + // d3.select(linked_geoms).data(data, key_fun); // WHY did we need this? + eActions(linked_geoms); + linkActions(elements); + }else{ + // elements are e.g. + eActions(elements); // Set the attributes of all elements (enter/exit/stay) + } + }; + + + + var value_tostring = function(selected_values) { + //function that is helpful to change the format of the string + var selector_url="#" + for (var selc_var in selected_values){ + if(selected_values.hasOwnProperty(selc_var)){ + var values_str=selected_values[selc_var].join(); + var sub_url=selc_var.concat("=","{",values_str,"}"); + selector_url=selector_url.concat(sub_url); + } + } + var url_nohash=window.location.href.match(/(^[^#]*)/)[0]; + selector_url=url_nohash.concat(selector_url); + return selector_url; + }; + + var get_values=function(){ + // function that is useful to get the selected values + var selected_values={} + for(var s_name in Selectors){ + var s_info=Selectors[s_name]; + var initial_selections = []; + if(s_info.type==="single"){ + initial_selections=[s_info.selected]; + } + else{ + for(var i in s_info.selected) { + initial_selections[i] = s_info.selected[i]; + } + } + selected_values[s_name]=initial_selections; + } + return selected_values; + }; + + var counter=-1; + var update_selector_url = function() { + var selected_values=get_values(); + var url=value_tostring(selected_values); + if(counter===-1){ + $(".table_selector_widgets").after(""); + $(".selectorurl").append("

Current URL

"); + $(".selectorurl").append("
"); + counter++; + } + $(".selectorurl a").attr("href",url).text(url); + }; + + // update scales for the plots that have update_axes option in + // theme_animint + function update_scales(p_name, axes, v_name, value){ + // Get pre-computed domain + var axis_domains = Plots[p_name]["axis_domains"]; + if(!isArray(axes)){ + axes = [axes]; + } + if(axis_domains != null){ + axes.forEach(function(xyaxis){ + // For Each PANEL, update the axes + Plots[p_name].layout.PANEL.forEach(function(panel_i, i){ + // Determine whether this panel has a scale or not + // If not we just update the scales according to the common + // scale and skip the updating of axis + var draw_axes = Plots[p_name].layout["AXIS_"+ xyaxis.toUpperCase()][i]; + if(draw_axes){ + var use_panel = panel_i; + }else{ + var use_panel = Plots[p_name].layout.PANEL[0]; + } + // We update the current selection of the plot every time + // and use it to index the correct domain + var curr_select = axis_domains[xyaxis].curr_select; + if(axis_domains[xyaxis].selectors.indexOf(v_name) > -1){ + curr_select[v_name] = value; + var str = use_panel+"."; + for(selec in curr_select){ + str = str + curr_select[selec] + "_"; + } + str = str.substring(0, str.length - 1); // Strip off trailing underscore + var use_domain = axis_domains[xyaxis]["domains"][str]; + } + if(use_domain != null){ + Plots[p_name]["scales"][panel_i][xyaxis].domain(use_domain); + var scales = Plots[p_name]["scales"][panel_i][xyaxis]; + // major and minor grid lines as calculated in the compiler + var grid_vals = Plots[p_name]["axis_domains"][xyaxis]["grids"][str]; + + // Once scales are updated, update the axis ticks if needed + if(draw_axes){ + // Tick values are same as major grid lines + update_axes(p_name, xyaxis, panel_i, grid_vals[1]); + } + // Update major and minor grid lines + update_grids(p_name, xyaxis, panel_i, grid_vals, scales); + } + }); + }); + } + } + + // Update the axis ticks etc. once plot is zoomed in/out + // currently called from update_scales. + function update_axes(p_name, axes, panel_i, tick_vals){ + var orientation; + if(axes == "x"){ + orientation = "bottom"; + }else{ + orientation = "left"; + } + if(!isArray(tick_vals)){ + tick_vals = [tick_vals]; + } + var xyaxis = d3.svg.axis() + .scale(Plots[p_name]["scales"][panel_i][axes]) + .orient(orientation) + .tickValues(tick_vals); + // update existing axis + var xyaxis_g = element.select("#plot_"+p_name).select("."+axes+"axis_"+panel_i) + .transition() + .duration(1000) + .call(xyaxis); + } + + // Update major/minor grids once axes ticks have been updated + function update_grids(p_name, axes, panel_i, grid_vals, scales){ + // Select panel to update + var bgr = element.select("#plot_"+p_name).select(".bgr"+panel_i); + + var orient; + if(axes == "x"){ + orient = "vert"; + }else{ + orient = "hor"; + } + + // Update major and minor grid lines + ["minor", "major"].forEach(function(grid_class, j){ + var lines = bgr.select(".grid_"+grid_class).select("."+orient); + var xy1, xy2; + if(axes == "x"){ + xy1 = lines.select("line").attr("y1"); + xy2 = lines.select("line").attr("y2"); + }else{ + xy1 = lines.select("line").attr("x1"); + xy2 = lines.select("line").attr("x2"); + } + + // Get default values for grid lines like colour, stroke etc. + var grid_background = Plots[p_name]["grid_"+grid_class]; + var col = grid_background.colour; + var lt = grid_background.linetype; + var size = grid_background.size; + var cap = grid_background.lineend; + + // Remove old lines + lines.selectAll("line") + .remove(); + + if(!isArray(grid_vals[j])){ + grid_vals[j] = [grid_vals[j]]; + } + + if(axes == "x"){ + lines.selectAll("line") + .data(grid_vals[j]) + .enter() + .append("line") + .attr("y1", xy1) + .attr("y2", xy2) + .attr("x1", function(d) { return scales(d); }) + .attr("x2", function(d) { return scales(d); }) + .style("stroke", col) + .style("stroke-linecap", cap) + .style("stroke-width", size) + .style("stroke-dasharray", function() { + return linetypesize2dasharray(lt, size); + }); + }else{ + lines.selectAll("line") + .data(grid_vals[j]) + .enter() + .append("line") + .attr("x1", xy1) + .attr("x2", xy2) + .attr("y1", function(d) { return scales(d); }) + .attr("y2", function(d) { return scales(d); }) + .style("stroke", col) + .style("stroke-linecap", cap) + .style("stroke-width", size) + .style("stroke-dasharray", function() { + return linetypesize2dasharray(lt, size); + }); + } + }); + } + + var update_selector = function (v_name, value) { + value = value + ""; + var s_info = Selectors[v_name]; + + if(s_info.type == "single"){ + // value is the new selection. + s_info.selected = value; + }else{ + // value should be added or removed from the selection. + var i_value = s_info.selected.indexOf(value); + if(i_value == -1){ + // not found, add to selection. + s_info.selected.push(value); + }else{ + // found, remove from selection. + s_info.selected.splice(i_value, 1); + } + } + update_selector_url() + // if there are levels, then there is a selectize widget which + // should be updated. + if(isArray(s_info.levels)){ + // the jquery ids + if(s_info.type == "single") { + var selected_ids = v_name.concat("___", value); + } else { + var selected_ids = []; + for(i in s_info.selected) { + selected_ids[i] = v_name.concat("___", s_info.selected[i]); + } + } + // from + // https://github.com/brianreavis/selectize.js/blob/master/docs/api.md: + // setValue(value, silent) If "silent" is truthy, no change + // event will be fired on the original input. + selectized_array[v_name].setValue(selected_ids, true); + } + + // For each updated geom, check if the axes of the plot need to be + // updated and update them + s_info.update.forEach(function(g_name){ + var plot_name = g_name.split("_").pop(); + var axes = Plots[plot_name]["options"]["update_axes"]; + if(axes != null){ + update_scales(plot_name, axes, v_name, value); + } + }); + + update_legend_opacity(v_name); + s_info.update.forEach(function(g_name){ + update_geom(g_name, v_name); + }); + }; + + var ifSelectedElse = function (s_value, s_name, selected, not_selected) { + var is_selected; + var s_info = Selectors[s_name]; + if(s_info.type == "single"){ + is_selected = s_value == s_info.selected; + }else{ + is_selected = s_info.selected.indexOf(s_value) != -1; + } + if(is_selected){ + return selected; + } else { + return not_selected; + } + }; + + function update_next_animation(){ + var values = d3.values(Animation.done_geoms); + if(d3.sum(values) == values.length){ + // If the values in done_geoms are all 1, then we have loaded + // all of the animation-related chunks, and we can start + // playing the animation. + var v_name = Animation.variable; + var cur = Selectors[v_name].selected; + var next = Animation.next[cur]; + update_selector(v_name, next); + } + } + + // The main idea of how legends work: + + // 1. In getLegend in animint.R I export the legend entries as a + // list of rows that can be used in a data() bind in D3. + + // 2. Here in add_legend I create a for every legend, and + // then I bind the legend entries to , \s*$/g,ra={option:[1,""],legend:[1,"
","
"],area:[1,"",""],param:[1,"",""],thead:[1,"
, and elements. + var add_legend = function(p_name, p_info){ + // case of multiple legends, d3 reads legend structure in as an array + var tdRight = element.select("td."+p_name+"_legend"); + var legendkeys = d3.keys(p_info.legend); + for(var i=0; i-1){ + // aesthetics that would draw a rect + legend_svgs.append("rect") + .attr("x", 2) + .attr("y", 2) + .attr("width", 10) + .attr("height", 10) + .style("stroke-width", function(d){return d["polygonsize"]||1;}) + .style("stroke-dasharray", function(d){ + return linetypesize2dasharray(d["polygonlinetype"], d["size"]||2); + }) + .style("stroke", function(d){return d["polygoncolour"] || "#000000";}) + .style("fill", function(d){return d["polygonfill"] || "#FFFFFF";}) + .style("opacity", function(d){return d["polygonalpha"]||1;}); + } + if(l_info.geoms.indexOf("text")>-1){ + // aesthetics that would draw a rect + legend_svgs.append("text") + .attr("x", 10) + .attr("y", 14) + .style("fill", function(d){return d["textcolour"]||1;}) + .style("text-anchor", "middle") + .attr("font-size", function(d){return d["textsize"]||1;}) + .text("a"); + } + if(l_info.geoms.indexOf("path")>-1){ + // aesthetics that would draw a line + legend_svgs.append("line") + .attr("x1", 1).attr("x2", 19).attr("y1", 7).attr("y2", 7) + .style("stroke-width", function(d){ + return linescale(d["pathsize"])||2; + }) + .style("stroke-dasharray", function(d){ + return linetypesize2dasharray(d["pathlinetype"], d["pathsize"] || 2); + }) + .style("stroke", function(d){return d["pathcolour"] || "#000000";}) + .style("opacity", function(d){return d["pathalpha"]||1;}); + } + if(l_info.geoms.indexOf("point")>-1){ + // aesthetics that would draw a point + legend_svgs.append("circle") + .attr("cx", 10) + .attr("cy", 7) + .attr("r", function(d){return pointscale(d["pointsize"])||4;}) + .style("stroke", function(d){return d["pointcolour"] || "#000000";}) + .style("fill", function(d){ + return d["pointfill"] || d["pointcolour"] || "#000000"; + }) + .style("opacity", function(d){return d["pointalpha"]||1;}); + } + legend_rows.append("td") + .attr("align", "left") // TODO: right for numbers? + .attr("class", "legend_entry_label") + .attr("id", function(d){ return d["id"]+"_label"; }) + .text(function(d){ return d["label"];}); + } + } + + // Download the main description of the interactive plot. + d3.json(json_file, function (error, response) { + if(response.hasOwnProperty("title")){ + // This selects the title of the web page, outside of wherever + // the animint is defined, usually a
-- so it is OK to use + // global d3.select here. + d3.select("title").text(response.title); + } + // Add plots. + for (var p_name in response.plots) { + add_plot(p_name, response.plots[p_name]); + add_legend(p_name, response.plots[p_name]); + // Append style sheet to document head. + css.appendChild(document.createTextNode(styles.join(" "))); + document.head.appendChild(css); + } + // Then add selectors and start downloading the first data subset. + for (var s_name in response.selectors) { + add_selector(s_name, response.selectors[s_name]); + } + + // Update the scales/axes of the plots if needed + // We do this so that the plots zoom in initially after loading + for (var p_name in response.plots) { + if(response.plots[p_name].axis_domains !== null){ + for(var xy in response.plots[p_name].axis_domains){ + var selectors = response.plots[p_name].axis_domains[xy].selectors; + if(!isArray(selectors)){ + selectors = [selectors]; + } + update_scales(p_name, xy, selectors[0], + response.selectors[selectors[0]].selected); + } + } + } + + //////////////////////////////////////////// + // Widgets at bottom of page + //////////////////////////////////////////// + element.append("br"); + + // loading table. + var show_hide_table = element.append("button") + .text("Show download status table"); + show_hide_table + .on("click", function(){ + if(this.textContent == "Show download status table"){ + loading.style("display", ""); + show_hide_table.text("Hide download status table"); + }else{ + loading.style("display", "none"); + show_hide_table.text("Show download status table"); + } + }); + var loading = element.append("table") + .style("display", "none"); + Widgets["loading"] = loading; + var tr = loading.append("tr"); + tr.append("th").text("geom"); + tr.append("th").attr("class", "chunk").text("selected chunk"); + tr.append("th").attr("class", "downloaded").text("downloaded"); + tr.append("th").attr("class", "total").text("total"); + tr.append("th").attr("class", "status").text("status"); + + // Add geoms and construct nest operators. + for (var g_name in response.geoms) { + add_geom(g_name, response.geoms[g_name]); + } + + // Animation control widgets. + var show_message = "Show animation controls"; + // add a button to view the animation widgets + var show_hide_animation_controls = element.append("button") + .text(show_message) + .attr("id", viz_id + "_show_hide_animation_controls") + .on("click", function(){ + if(this.textContent == show_message){ + time_table.style("display", ""); + show_hide_animation_controls.text("Hide animation controls"); + }else{ + time_table.style("display", "none"); + show_hide_animation_controls.text(show_message); + } + }) + ; + // table of the animint widgets + var time_table = element.append("table") + .style("display", "none"); + var first_tr = time_table.append("tr"); + var first_th = first_tr.append("th"); + // if there's a time variable, add a button to pause the animint + if(response.time){ + Animation.next = {}; + Animation.ms = response.time.ms; + Animation.variable = response.time.variable; + Animation.sequence = response.time.sequence; + Widgets["play_pause"] = first_th.append("button") + .text("Play") + .attr("id", "play_pause") + .on("click", function(){ + if(this.textContent == "Play"){ + Animation.play(); + }else{ + Animation.pause(false); + } + }) + ; + } + first_tr.append("th").text("milliseconds"); + if(response.time){ + var second_tr = time_table.append("tr"); + second_tr.append("td").text("updates"); + second_tr.append("td").append("input") + .attr("id", "updates_ms") + .attr("type", "text") + .attr("value", Animation.ms) + .on("change", function(){ + Animation.pause(false); + Animation.ms = this.value; + Animation.play(); + }) + ; + } + for(s_name in Selectors){ + var s_info = Selectors[s_name]; + if(!s_info.hasOwnProperty("duration")){ + s_info.duration = 0; + } + } + var selector_array = d3.keys(Selectors); + var duration_rows = time_table.selectAll("tr.duration") + .data(selector_array) + .enter() + .append("tr"); + duration_rows + .append("td") + .text(function(s_name){return s_name;}); + var duration_tds = duration_rows.append("td"); + var duration_inputs = duration_tds + .append("input") + .attr("id", function(s_name){ + return viz_id + "_duration_ms_" + s_name; + }) + .attr("type", "text") + .on("change", function(s_name){ + Selectors[s_name].duration = this.value; + }) + .attr("value", function(s_name){ + return Selectors[s_name].duration; + }); + // selector widgets + var toggle_message = "Show selection menus"; + var show_or_hide_fun = function(){ + if(this.textContent == toggle_message){ + selector_table.style("display", ""); + show_hide_selector_widgets.text("Hide selection menus"); + d3.select(".urltable").style("display","") + }else{ + selector_table.style("display", "none"); + show_hide_selector_widgets.text(toggle_message); + d3.select(".urltable").style("display","none") + } + } + var show_hide_selector_widgets = element.append("button") + .text(toggle_message) + .attr("class", "show_hide_selector_widgets") + .on("click", show_or_hide_fun) + ; + // adding a table for selector widgets + var selector_table = element.append("table") + .style("display", "none") + .attr("class", "table_selector_widgets") + ; + var selector_first_tr = selector_table.append("tr"); + selector_first_tr + .append("th") + .text("Variable") + ; + selector_first_tr + .append("th") + .text("Selected value(s)") + ; + + // looping through and adding a row for each selector + for(s_name in Selectors) { + var s_info = Selectors[s_name]; + // for .variable .value selectors, levels is undefined and we do + // not want to make a selectize widget. + + // TODO: why does it take so long to initialize the selectize + // widget when there are many (>1000) values? + if(isArray(s_info.levels)){ + // If there were no geoms that specified clickSelects for this + // selector, then there is no way to select it other than the + // selectize widgets (and possibly legends). So in this case + // we show the selectize widgets by default. + var selector_widgets_hidden = + show_hide_selector_widgets.text() == toggle_message; + var has_no_clickSelects = + !Selectors[s_name].hasOwnProperty("clickSelects") + var has_no_legend = + !Selectors[s_name].hasOwnProperty("legend") + if(selector_widgets_hidden && has_no_clickSelects && has_no_legend){ + var node = show_hide_selector_widgets.node(); + show_or_hide_fun.apply(node); + } + // removing "." from name so it can be used in ids + var s_name_id = legend_class_name(s_name); + + // adding a row for each selector + var selector_widget_row = selector_table + .append("tr") + .attr("class", function() { return s_name_id + "_selector_widget"; }) + ; + selector_widget_row.append("td").text(s_name); + // adding the selector + var selector_widget_select = selector_widget_row + .append("td") + .append("select") + .attr("class", function() { return s_name_id + "_input"; }) + .attr("placeholder", function() { return "Toggle " + s_name; }); + // adding an option for each level of the variable + selector_widget_select.selectAll("option") + .data(s_info.levels) + .enter() + .append("option") + .attr("value", function(d) { return d; }) + .text(function(d) { return d; }); + // making sure that the first option is blank + selector_widget_select + .insert("option") + .attr("value", "") + .text(function() { return "Toggle " + s_name; }); + + // calling selectize + var selectize_selector = to_select + ' .' + s_name_id + "_input"; + if(s_info.type == "single") { + // setting up array of selector and options + var selector_values = []; + for(i in s_info.levels) { + selector_values[i] = { + id: s_name.concat("___", s_info.levels[i]), + text: s_info.levels[i] + }; + } + // the id of the first selector + var selected_id = s_name.concat("___", s_info.selected); + + // if single selection, only allow one item + var $temp = $(selectize_selector) + .selectize({ + create: false, + valueField: 'id', + labelField: 'text', + searchField: ['text'], + options: selector_values, + items: [selected_id], + maxItems: 1, + allowEmptyOption: true, + onChange: function(value) { + // extracting the name and the level to update + var selector_name = value.split("___")[0]; + var selected_level = value.split("___")[1]; + // updating the selector + update_selector(selector_name, selected_level); + } + }) + ; + } else { // multiple selection: + // setting up array of selector and options + var selector_values = []; + if(typeof s_info.levels == "object") { + for(i in s_info.levels) { + selector_values[i] = { + id: s_name.concat("___", s_info.levels[i]), + text: s_info.levels[i] + }; + } + } else { + selector_values[0] = { + id: s_name.concat("___", s_info.levels), + text: s_info.levels + }; + } + // setting up an array to contain the initally selected elements + var initial_selections = []; + for(i in s_info.selected) { + initial_selections[i] = s_name.concat("___", s_info.selected[i]); + } + + // construct the selectize + var $temp = $(selectize_selector) + .selectize({ + create: false, + valueField: 'id', + labelField: 'text', + searchField: ['text'], + options: selector_values, + items: initial_selections, + maxItems: s_info.levels.length, + allowEmptyOption: true, + onChange: function(value) { + // if nothing is selected, remove what is currently selected + if(value == null) { + // extracting the selector ids from the options + var the_ids = Object.keys($(this)[0].options); + // the name of the appropriate selector + var selector_name = the_ids[0].split("___")[0]; + // the previously selected elements + var old_selections = Selectors[selector_name].selected; + // updating the selector for each of the old selections + old_selections.forEach(function(element) { + update_selector(selector_name, element); + }); + } else { // value is not null: + // grabbing the name of the selector from the selected value + var selector_name = value[0].split("___")[0]; + // identifying the levels that should be selected + var specified_levels = []; + for(i in value) { + specified_levels[i] = value[i].split("___")[1]; + } + // the previously selected entries + old_selections = Selectors[selector_name].selected; + + // the levels that need to have selections turned on + specified_levels + .filter(function(n) { + return old_selections.indexOf(n) == -1; + }) + .forEach(function(element) { + update_selector(selector_name, element); + }) + ; + // the levels that need to be turned off + // - same approach + old_selections + .filter(function(n) { + return specified_levels.indexOf(n) == -1; + }) + .forEach(function(element) { + update_selector(selector_name, element); + }) + ; + }//value==null + }//onChange + })//selectize + ; + }//single or multiple selection. + selectized_array[s_name] = $temp[0].selectize; + }//levels, is.variable.value + } // close for loop through selector widgets + // If this is an animation, then start downloading all the rest of + // the data, and start the animation. + if (response.time) { + var i, prev, cur; + for (var i = 0; i < Animation.sequence.length; i++) { + if (i == 0) { + prev = Animation.sequence[Animation.sequence.length-1]; + } else { + prev = Animation.sequence[i - 1]; + } + cur = Animation.sequence[i]; + Animation.next[prev] = cur; + } + Animation.timer = null; + Animation.play = function(){ + if(Animation.timer == null){ // only play if not already playing. + // as shown on http://bl.ocks.org/mbostock/3808234 + Animation.timer = setInterval(update_next_animation, Animation.ms); + Widgets["play_pause"].text("Pause"); + } + }; + Animation.play_after_visible = false; + Animation.pause = function(play_after_visible){ + Animation.play_after_visible = play_after_visible; + clearInterval(Animation.timer); + Animation.timer = null; + Widgets["play_pause"].text("Play"); + }; + var s_info = Selectors[Animation.variable]; + Animation.done_geoms = {}; + s_info.update.forEach(function(g_name){ + var g_info = Geoms[g_name]; + if(g_info.chunk_order.length == 1 && + g_info.chunk_order[0] == Animation.variable){ + g_info.seq_i = Animation.sequence.indexOf(s_info.selected); + g_info.seq_count = 0; + Animation.done_geoms[g_name] = 0; + download_next(g_name); + } + }); + Animation.play(); + all_geom_names = d3.keys(response.geoms); + + // This code starts/stops the animation timer when the page is + // hidden, inspired by + // http://stackoverflow.com/questions/1060008 + function onchange (evt) { + if(document.visibilityState == "visible"){ + if(Animation.play_after_visible){ + Animation.play(); + } + }else{ + if(Widgets["play_pause"].text() == "Pause"){ + Animation.pause(true); + } + } + }; + document.addEventListener("visibilitychange", onchange); + } + update_selector_url() + var check_func=function(){ + var status_array = $('.status').map(function(){ + return $.trim($(this).text()); + }).get(); + status_array=status_array.slice(1) + return status_array.every(function(elem){ return elem === "displayed"}); + } + if(window.location.hash) { + var fragment=window.location.hash; + fragment=fragment.slice(1); + fragment=decodeURI(fragment) + var frag_array=fragment.split(/(.*?})/); + frag_array=frag_array.filter(function(x){ return x!=""}) + frag_array.forEach(function(selector_string){ + var selector_hash=selector_string.split("="); + var selector_nam=selector_hash[0]; + var selector_values=selector_hash[1]; + var re = /\{(.*?)\}/; + selector_values=re.exec(selector_values)[1]; + var array_values = selector_values.split(','); + var s_info=Selectors[selector_nam] + if(s_info.type=="single"){ + + array_values.forEach(function(element) { + + wait_until_then(100, check_func, update_selector,selector_nam,element) + if(response.time)Animation.pause(true) + }); + + } + else{ + var old_selections = Selectors[selector_nam].selected; + // the levels that need to have selections turned on + array_values + .filter(function(n) { + return old_selections.indexOf(n) == -1; + }) + .forEach(function(element) { + wait_until_then(100, check_func, update_selector,selector_nam,element) + if(response.time){ + Animation.pause(true) + } + }); + + + old_selections + .filter(function(n) { + return array_values.indexOf(n) == -1; + }) + .forEach(function(element) { + wait_until_then(100, check_func, update_selector,selector_nam,element) + if(response.time){ + Animation.pause(true) + } + }); + } + + }) + } + }); +}; + + diff --git a/inst/htmljs/index.html b/inst/htmljs/index.html new file mode 100644 index 000000000..579135c0e --- /dev/null +++ b/inst/htmljs/index.html @@ -0,0 +1,27 @@ + + + + + + + Interactive animation + + + + + + + + + + + +
+ + + + + + diff --git a/inst/htmljs/scripts.html b/inst/htmljs/scripts.html new file mode 100644 index 000000000..416cbcb86 --- /dev/null +++ b/inst/htmljs/scripts.html @@ -0,0 +1,9 @@ + + + +
+ + + diff --git a/inst/htmljs/styles.css b/inst/htmljs/styles.css new file mode 100644 index 000000000..e69de29bb diff --git a/inst/htmljs/vendor/d3.v3.js b/inst/htmljs/vendor/d3.v3.js new file mode 100644 index 000000000..0f67082a6 --- /dev/null +++ b/inst/htmljs/vendor/d3.v3.js @@ -0,0 +1,7816 @@ +// Copyright (c) 2013, Michael Bostock +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: + +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. + +// * 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. + +// * The name Michael Bostock may not 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 MICHAEL BOSTOCK 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. +d3 = function() { + var π = Math.PI, ε = 1e-6, d3 = { + version: "3.0.6" + }, d3_radians = π / 180, d3_degrees = 180 / π, d3_document = document, d3_window = window; + function d3_target(d) { + return d.target; + } + function d3_source(d) { + return d.source; + } + var d3_format_decimalPoint = ".", d3_format_thousandsSeparator = ",", d3_format_grouping = [ 3, 3 ]; + if (!Date.now) Date.now = function() { + return +new Date(); + }; + try { + d3_document.createElement("div").style.setProperty("opacity", 0, ""); + } catch (error) { + var d3_style_prototype = d3_window.CSSStyleDeclaration.prototype, d3_style_setProperty = d3_style_prototype.setProperty; + d3_style_prototype.setProperty = function(name, value, priority) { + d3_style_setProperty.call(this, name, value + "", priority); + }; + } + function d3_class(ctor, properties) { + try { + for (var key in properties) { + Object.defineProperty(ctor.prototype, key, { + value: properties[key], + enumerable: false + }); + } + } catch (e) { + ctor.prototype = properties; + } + } + var d3_array = d3_arraySlice; + function d3_arrayCopy(pseudoarray) { + var i = -1, n = pseudoarray.length, array = []; + while (++i < n) array.push(pseudoarray[i]); + return array; + } + function d3_arraySlice(pseudoarray) { + return Array.prototype.slice.call(pseudoarray); + } + try { + d3_array(d3_document.documentElement.childNodes)[0].nodeType; + } catch (e) { + d3_array = d3_arrayCopy; + } + var d3_arraySubclass = [].__proto__ ? function(array, prototype) { + array.__proto__ = prototype; + } : function(array, prototype) { + for (var property in prototype) array[property] = prototype[property]; + }; + d3.map = function(object) { + var map = new d3_Map(); + for (var key in object) map.set(key, object[key]); + return map; + }; + function d3_Map() {} + d3_class(d3_Map, { + has: function(key) { + return d3_map_prefix + key in this; + }, + get: function(key) { + return this[d3_map_prefix + key]; + }, + set: function(key, value) { + return this[d3_map_prefix + key] = value; + }, + remove: function(key) { + key = d3_map_prefix + key; + return key in this && delete this[key]; + }, + keys: function() { + var keys = []; + this.forEach(function(key) { + keys.push(key); + }); + return keys; + }, + values: function() { + var values = []; + this.forEach(function(key, value) { + values.push(value); + }); + return values; + }, + entries: function() { + var entries = []; + this.forEach(function(key, value) { + entries.push({ + key: key, + value: value + }); + }); + return entries; + }, + forEach: function(f) { + for (var key in this) { + if (key.charCodeAt(0) === d3_map_prefixCode) { + f.call(this, key.substring(1), this[key]); + } + } + } + }); + var d3_map_prefix = "\0", d3_map_prefixCode = d3_map_prefix.charCodeAt(0); + function d3_identity(d) { + return d; + } + function d3_true() { + return true; + } + function d3_functor(v) { + return typeof v === "function" ? v : function() { + return v; + }; + } + d3.functor = d3_functor; + d3.rebind = function(target, source) { + var i = 1, n = arguments.length, method; + while (++i < n) target[method = arguments[i]] = d3_rebind(target, source, source[method]); + return target; + }; + function d3_rebind(target, source, method) { + return function() { + var value = method.apply(source, arguments); + return arguments.length ? target : value; + }; + } + d3.ascending = function(a, b) { + return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN; + }; + d3.descending = function(a, b) { + return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN; + }; + d3.mean = function(array, f) { + var n = array.length, a, m = 0, i = -1, j = 0; + if (arguments.length === 1) { + while (++i < n) if (d3_number(a = array[i])) m += (a - m) / ++j; + } else { + while (++i < n) if (d3_number(a = f.call(array, array[i], i))) m += (a - m) / ++j; + } + return j ? m : undefined; + }; + d3.median = function(array, f) { + if (arguments.length > 1) array = array.map(f); + array = array.filter(d3_number); + return array.length ? d3.quantile(array.sort(d3.ascending), .5) : undefined; + }; + d3.min = function(array, f) { + var i = -1, n = array.length, a, b; + if (arguments.length === 1) { + while (++i < n && ((a = array[i]) == null || a != a)) a = undefined; + while (++i < n) if ((b = array[i]) != null && a > b) a = b; + } else { + while (++i < n && ((a = f.call(array, array[i], i)) == null || a != a)) a = undefined; + while (++i < n) if ((b = f.call(array, array[i], i)) != null && a > b) a = b; + } + return a; + }; + d3.max = function(array, f) { + var i = -1, n = array.length, a, b; + if (arguments.length === 1) { + while (++i < n && ((a = array[i]) == null || a != a)) a = undefined; + while (++i < n) if ((b = array[i]) != null && b > a) a = b; + } else { + while (++i < n && ((a = f.call(array, array[i], i)) == null || a != a)) a = undefined; + while (++i < n) if ((b = f.call(array, array[i], i)) != null && b > a) a = b; + } + return a; + }; + d3.extent = function(array, f) { + var i = -1, n = array.length, a, b, c; + if (arguments.length === 1) { + while (++i < n && ((a = c = array[i]) == null || a != a)) a = c = undefined; + while (++i < n) if ((b = array[i]) != null) { + if (a > b) a = b; + if (c < b) c = b; + } + } else { + while (++i < n && ((a = c = f.call(array, array[i], i)) == null || a != a)) a = undefined; + while (++i < n) if ((b = f.call(array, array[i], i)) != null) { + if (a > b) a = b; + if (c < b) c = b; + } + } + return [ a, c ]; + }; + d3.random = { + normal: function(µ, σ) { + var n = arguments.length; + if (n < 2) σ = 1; + if (n < 1) µ = 0; + return function() { + var x, y, r; + do { + x = Math.random() * 2 - 1; + y = Math.random() * 2 - 1; + r = x * x + y * y; + } while (!r || r > 1); + return µ + σ * x * Math.sqrt(-2 * Math.log(r) / r); + }; + }, + logNormal: function() { + var random = d3.random.normal.apply(d3, arguments); + return function() { + return Math.exp(random()); + }; + }, + irwinHall: function(m) { + return function() { + for (var s = 0, j = 0; j < m; j++) s += Math.random(); + return s / m; + }; + } + }; + function d3_number(x) { + return x != null && !isNaN(x); + } + d3.sum = function(array, f) { + var s = 0, n = array.length, a, i = -1; + if (arguments.length === 1) { + while (++i < n) if (!isNaN(a = +array[i])) s += a; + } else { + while (++i < n) if (!isNaN(a = +f.call(array, array[i], i))) s += a; + } + return s; + }; + d3.quantile = function(values, p) { + var H = (values.length - 1) * p + 1, h = Math.floor(H), v = +values[h - 1], e = H - h; + return e ? v + e * (values[h] - v) : v; + }; + d3.shuffle = function(array) { + var m = array.length, t, i; + while (m) { + i = Math.random() * m-- | 0; + t = array[m], array[m] = array[i], array[i] = t; + } + return array; + }; + d3.transpose = function(matrix) { + return d3.zip.apply(d3, matrix); + }; + d3.zip = function() { + if (!(n = arguments.length)) return []; + for (var i = -1, m = d3.min(arguments, d3_zipLength), zips = new Array(m); ++i < m; ) { + for (var j = -1, n, zip = zips[i] = new Array(n); ++j < n; ) { + zip[j] = arguments[j][i]; + } + } + return zips; + }; + function d3_zipLength(d) { + return d.length; + } + d3.bisector = function(f) { + return { + left: function(a, x, lo, hi) { + if (arguments.length < 3) lo = 0; + if (arguments.length < 4) hi = a.length; + while (lo < hi) { + var mid = lo + hi >>> 1; + if (f.call(a, a[mid], mid) < x) lo = mid + 1; else hi = mid; + } + return lo; + }, + right: function(a, x, lo, hi) { + if (arguments.length < 3) lo = 0; + if (arguments.length < 4) hi = a.length; + while (lo < hi) { + var mid = lo + hi >>> 1; + if (x < f.call(a, a[mid], mid)) hi = mid; else lo = mid + 1; + } + return lo; + } + }; + }; + var d3_bisector = d3.bisector(function(d) { + return d; + }); + d3.bisectLeft = d3_bisector.left; + d3.bisect = d3.bisectRight = d3_bisector.right; + d3.nest = function() { + var nest = {}, keys = [], sortKeys = [], sortValues, rollup; + function map(array, depth) { + if (depth >= keys.length) return rollup ? rollup.call(nest, array) : sortValues ? array.sort(sortValues) : array; + var i = -1, n = array.length, key = keys[depth++], keyValue, object, valuesByKey = new d3_Map(), values, o = {}; + while (++i < n) { + if (values = valuesByKey.get(keyValue = key(object = array[i]))) { + values.push(object); + } else { + valuesByKey.set(keyValue, [ object ]); + } + } + valuesByKey.forEach(function(keyValue, values) { + o[keyValue] = map(values, depth); + }); + return o; + } + function entries(map, depth) { + if (depth >= keys.length) return map; + var a = [], sortKey = sortKeys[depth++], key; + for (key in map) { + a.push({ + key: key, + values: entries(map[key], depth) + }); + } + if (sortKey) a.sort(function(a, b) { + return sortKey(a.key, b.key); + }); + return a; + } + nest.map = function(array) { + return map(array, 0); + }; + nest.entries = function(array) { + return entries(map(array, 0), 0); + }; + nest.key = function(d) { + keys.push(d); + return nest; + }; + nest.sortKeys = function(order) { + sortKeys[keys.length - 1] = order; + return nest; + }; + nest.sortValues = function(order) { + sortValues = order; + return nest; + }; + nest.rollup = function(f) { + rollup = f; + return nest; + }; + return nest; + }; + d3.keys = function(map) { + var keys = []; + for (var key in map) keys.push(key); + return keys; + }; + d3.values = function(map) { + var values = []; + for (var key in map) values.push(map[key]); + return values; + }; + d3.entries = function(map) { + var entries = []; + for (var key in map) entries.push({ + key: key, + value: map[key] + }); + return entries; + }; + d3.permute = function(array, indexes) { + var permutes = [], i = -1, n = indexes.length; + while (++i < n) permutes[i] = array[indexes[i]]; + return permutes; + }; + d3.merge = function(arrays) { + return Array.prototype.concat.apply([], arrays); + }; + function d3_collapse(s) { + return s.trim().replace(/\s+/g, " "); + } + d3.range = function(start, stop, step) { + if (arguments.length < 3) { + step = 1; + if (arguments.length < 2) { + stop = start; + start = 0; + } + } + if ((stop - start) / step === Infinity) throw new Error("infinite range"); + var range = [], k = d3_range_integerScale(Math.abs(step)), i = -1, j; + start *= k, stop *= k, step *= k; + if (step < 0) while ((j = start + step * ++i) > stop) range.push(j / k); else while ((j = start + step * ++i) < stop) range.push(j / k); + return range; + }; + function d3_range_integerScale(x) { + var k = 1; + while (x * k % 1) k *= 10; + return k; + } + d3.requote = function(s) { + return s.replace(d3_requote_re, "\\$&"); + }; + var d3_requote_re = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g; + d3.round = function(x, n) { + return n ? Math.round(x * (n = Math.pow(10, n))) / n : Math.round(x); + }; + d3.xhr = function(url, mimeType, callback) { + var xhr = {}, dispatch = d3.dispatch("progress", "load", "error"), headers = {}, response = d3_identity, request = new (d3_window.XDomainRequest && /^(http(s)?:)?\/\//.test(url) ? XDomainRequest : XMLHttpRequest)(); + "onload" in request ? request.onload = request.onerror = respond : request.onreadystatechange = function() { + request.readyState > 3 && respond(); + }; + function respond() { + var s = request.status; + !s && request.responseText || s >= 200 && s < 300 || s === 304 ? dispatch.load.call(xhr, response.call(xhr, request)) : dispatch.error.call(xhr, request); + } + request.onprogress = function(event) { + var o = d3.event; + d3.event = event; + try { + dispatch.progress.call(xhr, request); + } finally { + d3.event = o; + } + }; + xhr.header = function(name, value) { + name = (name + "").toLowerCase(); + if (arguments.length < 2) return headers[name]; + if (value == null) delete headers[name]; else headers[name] = value + ""; + return xhr; + }; + xhr.mimeType = function(value) { + if (!arguments.length) return mimeType; + mimeType = value == null ? null : value + ""; + return xhr; + }; + xhr.response = function(value) { + response = value; + return xhr; + }; + [ "get", "post" ].forEach(function(method) { + xhr[method] = function() { + return xhr.send.apply(xhr, [ method ].concat(d3_array(arguments))); + }; + }); + xhr.send = function(method, data, callback) { + if (arguments.length === 2 && typeof data === "function") callback = data, data = null; + request.open(method, url, true); + if (mimeType != null && !("accept" in headers)) headers["accept"] = mimeType + ",*/*"; + if (request.setRequestHeader) for (var name in headers) request.setRequestHeader(name, headers[name]); + if (mimeType != null && request.overrideMimeType) request.overrideMimeType(mimeType); + if (callback != null) xhr.on("error", callback).on("load", function(request) { + callback(null, request); + }); + request.send(data == null ? null : data); + return xhr; + }; + xhr.abort = function() { + request.abort(); + return xhr; + }; + d3.rebind(xhr, dispatch, "on"); + if (arguments.length === 2 && typeof mimeType === "function") callback = mimeType, + mimeType = null; + return callback == null ? xhr : xhr.get(d3_xhr_fixCallback(callback)); + }; + function d3_xhr_fixCallback(callback) { + return callback.length === 1 ? function(error, request) { + callback(error == null ? request : null); + } : callback; + } + d3.text = function() { + return d3.xhr.apply(d3, arguments).response(d3_text); + }; + function d3_text(request) { + return request.responseText; + } + d3.json = function(url, callback) { + return d3.xhr(url, "application/json", callback).response(d3_json); + }; + function d3_json(request) { + return JSON.parse(request.responseText); + } + d3.html = function(url, callback) { + return d3.xhr(url, "text/html", callback).response(d3_html); + }; + function d3_html(request) { + var range = d3_document.createRange(); + range.selectNode(d3_document.body); + return range.createContextualFragment(request.responseText); + } + d3.xml = function() { + return d3.xhr.apply(d3, arguments).response(d3_xml); + }; + function d3_xml(request) { + return request.responseXML; + } + var d3_nsPrefix = { + svg: "http://www.w3.org/2000/svg", + xhtml: "http://www.w3.org/1999/xhtml", + xlink: "http://www.w3.org/1999/xlink", + xml: "http://www.w3.org/XML/1998/namespace", + xmlns: "http://www.w3.org/2000/xmlns/" + }; + d3.ns = { + prefix: d3_nsPrefix, + qualify: function(name) { + var i = name.indexOf(":"), prefix = name; + if (i >= 0) { + prefix = name.substring(0, i); + name = name.substring(i + 1); + } + return d3_nsPrefix.hasOwnProperty(prefix) ? { + space: d3_nsPrefix[prefix], + local: name + } : name; + } + }; + d3.dispatch = function() { + var dispatch = new d3_dispatch(), i = -1, n = arguments.length; + while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch); + return dispatch; + }; + function d3_dispatch() {} + d3_dispatch.prototype.on = function(type, listener) { + var i = type.indexOf("."), name = ""; + if (i > 0) { + name = type.substring(i + 1); + type = type.substring(0, i); + } + return arguments.length < 2 ? this[type].on(name) : this[type].on(name, listener); + }; + function d3_dispatch_event(dispatch) { + var listeners = [], listenerByName = new d3_Map(); + function event() { + var z = listeners, i = -1, n = z.length, l; + while (++i < n) if (l = z[i].on) l.apply(this, arguments); + return dispatch; + } + event.on = function(name, listener) { + var l = listenerByName.get(name), i; + if (arguments.length < 2) return l && l.on; + if (l) { + l.on = null; + listeners = listeners.slice(0, i = listeners.indexOf(l)).concat(listeners.slice(i + 1)); + listenerByName.remove(name); + } + if (listener) listeners.push(listenerByName.set(name, { + on: listener + })); + return dispatch; + }; + return event; + } + d3.format = function(specifier) { + var match = d3_format_re.exec(specifier), fill = match[1] || " ", align = match[2] || ">", sign = match[3] || "", basePrefix = match[4] || "", zfill = match[5], width = +match[6], comma = match[7], precision = match[8], type = match[9], scale = 1, suffix = "", integer = false; + if (precision) precision = +precision.substring(1); + if (zfill || fill === "0" && align === "=") { + zfill = fill = "0"; + align = "="; + if (comma) width -= Math.floor((width - 1) / 4); + } + switch (type) { + case "n": + comma = true; + type = "g"; + break; + + case "%": + scale = 100; + suffix = "%"; + type = "f"; + break; + + case "p": + scale = 100; + suffix = "%"; + type = "r"; + break; + + case "b": + case "o": + case "x": + case "X": + if (basePrefix) basePrefix = "0" + type.toLowerCase(); + + case "c": + case "d": + integer = true; + precision = 0; + break; + + case "s": + scale = -1; + type = "r"; + break; + } + if (basePrefix === "#") basePrefix = ""; + if (type == "r" && !precision) type = "g"; + type = d3_format_types.get(type) || d3_format_typeDefault; + var zcomma = zfill && comma; + return function(value) { + if (integer && value % 1) return ""; + var negative = value < 0 || value === 0 && 1 / value < 0 ? (value = -value, "-") : sign; + if (scale < 0) { + var prefix = d3.formatPrefix(value, precision); + value = prefix.scale(value); + suffix = prefix.symbol; + } else { + value *= scale; + } + value = type(value, precision); + if (!zfill && comma) value = d3_format_group(value); + var length = basePrefix.length + value.length + (zcomma ? 0 : negative.length), padding = length < width ? new Array(length = width - length + 1).join(fill) : ""; + if (zcomma) value = d3_format_group(padding + value); + if (d3_format_decimalPoint) value.replace(".", d3_format_decimalPoint); + negative += basePrefix; + return (align === "<" ? negative + value + padding : align === ">" ? padding + negative + value : align === "^" ? padding.substring(0, length >>= 1) + negative + value + padding.substring(length) : negative + (zcomma ? value : padding + value)) + suffix; + }; + }; + var d3_format_re = /(?:([^{])?([<>=^]))?([+\- ])?(#)?(0)?([0-9]+)?(,)?(\.[0-9]+)?([a-zA-Z%])?/; + var d3_format_types = d3.map({ + b: function(x) { + return x.toString(2); + }, + c: function(x) { + return String.fromCharCode(x); + }, + o: function(x) { + return x.toString(8); + }, + x: function(x) { + return x.toString(16); + }, + X: function(x) { + return x.toString(16).toUpperCase(); + }, + g: function(x, p) { + return x.toPrecision(p); + }, + e: function(x, p) { + return x.toExponential(p); + }, + f: function(x, p) { + return x.toFixed(p); + }, + r: function(x, p) { + return (x = d3.round(x, d3_format_precision(x, p))).toFixed(Math.max(0, Math.min(20, d3_format_precision(x * (1 + 1e-15), p)))); + } + }); + function d3_format_precision(x, p) { + return p - (x ? Math.ceil(Math.log(x) / Math.LN10) : 1); + } + function d3_format_typeDefault(x) { + return x + ""; + } + var d3_format_group = d3_identity; + if (d3_format_grouping) { + var d3_format_groupingLength = d3_format_grouping.length; + d3_format_group = function(value) { + var i = value.lastIndexOf("."), f = i >= 0 ? "." + value.substring(i + 1) : (i = value.length, + ""), t = [], j = 0, g = d3_format_grouping[0]; + while (i > 0 && g > 0) { + t.push(value.substring(i -= g, i + g)); + g = d3_format_grouping[j = (j + 1) % d3_format_groupingLength]; + } + return t.reverse().join(d3_format_thousandsSeparator || "") + f; + }; + } + var d3_formatPrefixes = [ "y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y" ].map(d3_formatPrefix); + d3.formatPrefix = function(value, precision) { + var i = 0; + if (value) { + if (value < 0) value *= -1; + if (precision) value = d3.round(value, d3_format_precision(value, precision)); + i = 1 + Math.floor(1e-12 + Math.log(value) / Math.LN10); + i = Math.max(-24, Math.min(24, Math.floor((i <= 0 ? i + 1 : i - 1) / 3) * 3)); + } + return d3_formatPrefixes[8 + i / 3]; + }; + function d3_formatPrefix(d, i) { + var k = Math.pow(10, Math.abs(8 - i) * 3); + return { + scale: i > 8 ? function(d) { + return d / k; + } : function(d) { + return d * k; + }, + symbol: d + }; + } + var d3_ease_default = function() { + return d3_identity; + }; + var d3_ease = d3.map({ + linear: d3_ease_default, + poly: d3_ease_poly, + quad: function() { + return d3_ease_quad; + }, + cubic: function() { + return d3_ease_cubic; + }, + sin: function() { + return d3_ease_sin; + }, + exp: function() { + return d3_ease_exp; + }, + circle: function() { + return d3_ease_circle; + }, + elastic: d3_ease_elastic, + back: d3_ease_back, + bounce: function() { + return d3_ease_bounce; + } + }); + var d3_ease_mode = d3.map({ + "in": d3_identity, + out: d3_ease_reverse, + "in-out": d3_ease_reflect, + "out-in": function(f) { + return d3_ease_reflect(d3_ease_reverse(f)); + } + }); + d3.ease = function(name) { + var i = name.indexOf("-"), t = i >= 0 ? name.substring(0, i) : name, m = i >= 0 ? name.substring(i + 1) : "in"; + t = d3_ease.get(t) || d3_ease_default; + m = d3_ease_mode.get(m) || d3_identity; + return d3_ease_clamp(m(t.apply(null, Array.prototype.slice.call(arguments, 1)))); + }; + function d3_ease_clamp(f) { + return function(t) { + return t <= 0 ? 0 : t >= 1 ? 1 : f(t); + }; + } + function d3_ease_reverse(f) { + return function(t) { + return 1 - f(1 - t); + }; + } + function d3_ease_reflect(f) { + return function(t) { + return .5 * (t < .5 ? f(2 * t) : 2 - f(2 - 2 * t)); + }; + } + function d3_ease_quad(t) { + return t * t; + } + function d3_ease_cubic(t) { + return t * t * t; + } + function d3_ease_cubicInOut(t) { + if (t <= 0) return 0; + if (t >= 1) return 1; + var t2 = t * t, t3 = t2 * t; + return 4 * (t < .5 ? t3 : 3 * (t - t2) + t3 - .75); + } + function d3_ease_poly(e) { + return function(t) { + return Math.pow(t, e); + }; + } + function d3_ease_sin(t) { + return 1 - Math.cos(t * π / 2); + } + function d3_ease_exp(t) { + return Math.pow(2, 10 * (t - 1)); + } + function d3_ease_circle(t) { + return 1 - Math.sqrt(1 - t * t); + } + function d3_ease_elastic(a, p) { + var s; + if (arguments.length < 2) p = .45; + if (arguments.length) s = p / (2 * π) * Math.asin(1 / a); else a = 1, s = p / 4; + return function(t) { + return 1 + a * Math.pow(2, 10 * -t) * Math.sin((t - s) * 2 * π / p); + }; + } + function d3_ease_back(s) { + if (!s) s = 1.70158; + return function(t) { + return t * t * ((s + 1) * t - s); + }; + } + function d3_ease_bounce(t) { + return t < 1 / 2.75 ? 7.5625 * t * t : t < 2 / 2.75 ? 7.5625 * (t -= 1.5 / 2.75) * t + .75 : t < 2.5 / 2.75 ? 7.5625 * (t -= 2.25 / 2.75) * t + .9375 : 7.5625 * (t -= 2.625 / 2.75) * t + .984375; + } + d3.event = null; + function d3_eventCancel() { + d3.event.stopPropagation(); + d3.event.preventDefault(); + } + function d3_eventSource() { + var e = d3.event, s; + while (s = e.sourceEvent) e = s; + return e; + } + function d3_eventDispatch(target) { + var dispatch = new d3_dispatch(), i = 0, n = arguments.length; + while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch); + dispatch.of = function(thiz, argumentz) { + return function(e1) { + try { + var e0 = e1.sourceEvent = d3.event; + e1.target = target; + d3.event = e1; + dispatch[e1.type].apply(thiz, argumentz); + } finally { + d3.event = e0; + } + }; + }; + return dispatch; + } + d3.transform = function(string) { + var g = d3_document.createElementNS(d3.ns.prefix.svg, "g"); + return (d3.transform = function(string) { + g.setAttribute("transform", string); + var t = g.transform.baseVal.consolidate(); + return new d3_transform(t ? t.matrix : d3_transformIdentity); + })(string); + }; + function d3_transform(m) { + var r0 = [ m.a, m.b ], r1 = [ m.c, m.d ], kx = d3_transformNormalize(r0), kz = d3_transformDot(r0, r1), ky = d3_transformNormalize(d3_transformCombine(r1, r0, -kz)) || 0; + if (r0[0] * r1[1] < r1[0] * r0[1]) { + r0[0] *= -1; + r0[1] *= -1; + kx *= -1; + kz *= -1; + } + this.rotate = (kx ? Math.atan2(r0[1], r0[0]) : Math.atan2(-r1[0], r1[1])) * d3_degrees; + this.translate = [ m.e, m.f ]; + this.scale = [ kx, ky ]; + this.skew = ky ? Math.atan2(kz, ky) * d3_degrees : 0; + } + d3_transform.prototype.toString = function() { + return "translate(" + this.translate + ")rotate(" + this.rotate + ")skewX(" + this.skew + ")scale(" + this.scale + ")"; + }; + function d3_transformDot(a, b) { + return a[0] * b[0] + a[1] * b[1]; + } + function d3_transformNormalize(a) { + var k = Math.sqrt(d3_transformDot(a, a)); + if (k) { + a[0] /= k; + a[1] /= k; + } + return k; + } + function d3_transformCombine(a, b, k) { + a[0] += k * b[0]; + a[1] += k * b[1]; + return a; + } + var d3_transformIdentity = { + a: 1, + b: 0, + c: 0, + d: 1, + e: 0, + f: 0 + }; + d3.interpolate = function(a, b) { + var i = d3.interpolators.length, f; + while (--i >= 0 && !(f = d3.interpolators[i](a, b))) ; + return f; + }; + d3.interpolateNumber = function(a, b) { + b -= a; + return function(t) { + return a + b * t; + }; + }; + d3.interpolateRound = function(a, b) { + b -= a; + return function(t) { + return Math.round(a + b * t); + }; + }; + d3.interpolateString = function(a, b) { + var m, i, j, s0 = 0, s1 = 0, s = [], q = [], n, o; + d3_interpolate_number.lastIndex = 0; + for (i = 0; m = d3_interpolate_number.exec(b); ++i) { + if (m.index) s.push(b.substring(s0, s1 = m.index)); + q.push({ + i: s.length, + x: m[0] + }); + s.push(null); + s0 = d3_interpolate_number.lastIndex; + } + if (s0 < b.length) s.push(b.substring(s0)); + for (i = 0, n = q.length; (m = d3_interpolate_number.exec(a)) && i < n; ++i) { + o = q[i]; + if (o.x == m[0]) { + if (o.i) { + if (s[o.i + 1] == null) { + s[o.i - 1] += o.x; + s.splice(o.i, 1); + for (j = i + 1; j < n; ++j) q[j].i--; + } else { + s[o.i - 1] += o.x + s[o.i + 1]; + s.splice(o.i, 2); + for (j = i + 1; j < n; ++j) q[j].i -= 2; + } + } else { + if (s[o.i + 1] == null) { + s[o.i] = o.x; + } else { + s[o.i] = o.x + s[o.i + 1]; + s.splice(o.i + 1, 1); + for (j = i + 1; j < n; ++j) q[j].i--; + } + } + q.splice(i, 1); + n--; + i--; + } else { + o.x = d3.interpolateNumber(parseFloat(m[0]), parseFloat(o.x)); + } + } + while (i < n) { + o = q.pop(); + if (s[o.i + 1] == null) { + s[o.i] = o.x; + } else { + s[o.i] = o.x + s[o.i + 1]; + s.splice(o.i + 1, 1); + } + n--; + } + if (s.length === 1) { + return s[0] == null ? q[0].x : function() { + return b; + }; + } + return function(t) { + for (i = 0; i < n; ++i) s[(o = q[i]).i] = o.x(t); + return s.join(""); + }; + }; + d3.interpolateTransform = function(a, b) { + var s = [], q = [], n, A = d3.transform(a), B = d3.transform(b), ta = A.translate, tb = B.translate, ra = A.rotate, rb = B.rotate, wa = A.skew, wb = B.skew, ka = A.scale, kb = B.scale; + if (ta[0] != tb[0] || ta[1] != tb[1]) { + s.push("translate(", null, ",", null, ")"); + q.push({ + i: 1, + x: d3.interpolateNumber(ta[0], tb[0]) + }, { + i: 3, + x: d3.interpolateNumber(ta[1], tb[1]) + }); + } else if (tb[0] || tb[1]) { + s.push("translate(" + tb + ")"); + } else { + s.push(""); + } + if (ra != rb) { + if (ra - rb > 180) rb += 360; else if (rb - ra > 180) ra += 360; + q.push({ + i: s.push(s.pop() + "rotate(", null, ")") - 2, + x: d3.interpolateNumber(ra, rb) + }); + } else if (rb) { + s.push(s.pop() + "rotate(" + rb + ")"); + } + if (wa != wb) { + q.push({ + i: s.push(s.pop() + "skewX(", null, ")") - 2, + x: d3.interpolateNumber(wa, wb) + }); + } else if (wb) { + s.push(s.pop() + "skewX(" + wb + ")"); + } + if (ka[0] != kb[0] || ka[1] != kb[1]) { + n = s.push(s.pop() + "scale(", null, ",", null, ")"); + q.push({ + i: n - 4, + x: d3.interpolateNumber(ka[0], kb[0]) + }, { + i: n - 2, + x: d3.interpolateNumber(ka[1], kb[1]) + }); + } else if (kb[0] != 1 || kb[1] != 1) { + s.push(s.pop() + "scale(" + kb + ")"); + } + n = q.length; + return function(t) { + var i = -1, o; + while (++i < n) s[(o = q[i]).i] = o.x(t); + return s.join(""); + }; + }; + d3.interpolateRgb = function(a, b) { + a = d3.rgb(a); + b = d3.rgb(b); + var ar = a.r, ag = a.g, ab = a.b, br = b.r - ar, bg = b.g - ag, bb = b.b - ab; + return function(t) { + return "#" + d3_rgb_hex(Math.round(ar + br * t)) + d3_rgb_hex(Math.round(ag + bg * t)) + d3_rgb_hex(Math.round(ab + bb * t)); + }; + }; + d3.interpolateHsl = function(a, b) { + a = d3.hsl(a); + b = d3.hsl(b); + var h0 = a.h, s0 = a.s, l0 = a.l, h1 = b.h - h0, s1 = b.s - s0, l1 = b.l - l0; + if (h1 > 180) h1 -= 360; else if (h1 < -180) h1 += 360; + return function(t) { + return d3_hsl_rgb(h0 + h1 * t, s0 + s1 * t, l0 + l1 * t) + ""; + }; + }; + d3.interpolateLab = function(a, b) { + a = d3.lab(a); + b = d3.lab(b); + var al = a.l, aa = a.a, ab = a.b, bl = b.l - al, ba = b.a - aa, bb = b.b - ab; + return function(t) { + return d3_lab_rgb(al + bl * t, aa + ba * t, ab + bb * t) + ""; + }; + }; + d3.interpolateHcl = function(a, b) { + a = d3.hcl(a); + b = d3.hcl(b); + var ah = a.h, ac = a.c, al = a.l, bh = b.h - ah, bc = b.c - ac, bl = b.l - al; + if (bh > 180) bh -= 360; else if (bh < -180) bh += 360; + return function(t) { + return d3_hcl_lab(ah + bh * t, ac + bc * t, al + bl * t) + ""; + }; + }; + d3.interpolateArray = function(a, b) { + var x = [], c = [], na = a.length, nb = b.length, n0 = Math.min(a.length, b.length), i; + for (i = 0; i < n0; ++i) x.push(d3.interpolate(a[i], b[i])); + for (;i < na; ++i) c[i] = a[i]; + for (;i < nb; ++i) c[i] = b[i]; + return function(t) { + for (i = 0; i < n0; ++i) c[i] = x[i](t); + return c; + }; + }; + d3.interpolateObject = function(a, b) { + var i = {}, c = {}, k; + for (k in a) { + if (k in b) { + i[k] = d3_interpolateByName(k)(a[k], b[k]); + } else { + c[k] = a[k]; + } + } + for (k in b) { + if (!(k in a)) { + c[k] = b[k]; + } + } + return function(t) { + for (k in i) c[k] = i[k](t); + return c; + }; + }; + var d3_interpolate_number = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g; + function d3_interpolateByName(name) { + return name == "transform" ? d3.interpolateTransform : d3.interpolate; + } + d3.interpolators = [ d3.interpolateObject, function(a, b) { + return b instanceof Array && d3.interpolateArray(a, b); + }, function(a, b) { + return (typeof a === "string" || typeof b === "string") && d3.interpolateString(a + "", b + ""); + }, function(a, b) { + return (typeof b === "string" ? d3_rgb_names.has(b) || /^(#|rgb\(|hsl\()/.test(b) : b instanceof d3_Color) && d3.interpolateRgb(a, b); + }, function(a, b) { + return !isNaN(a = +a) && !isNaN(b = +b) && d3.interpolateNumber(a, b); + } ]; + function d3_uninterpolateNumber(a, b) { + b = b - (a = +a) ? 1 / (b - a) : 0; + return function(x) { + return (x - a) * b; + }; + } + function d3_uninterpolateClamp(a, b) { + b = b - (a = +a) ? 1 / (b - a) : 0; + return function(x) { + return Math.max(0, Math.min(1, (x - a) * b)); + }; + } + function d3_Color() {} + d3_Color.prototype.toString = function() { + return this.rgb() + ""; + }; + d3.rgb = function(r, g, b) { + return arguments.length === 1 ? r instanceof d3_Rgb ? d3_rgb(r.r, r.g, r.b) : d3_rgb_parse("" + r, d3_rgb, d3_hsl_rgb) : d3_rgb(~~r, ~~g, ~~b); + }; + function d3_rgb(r, g, b) { + return new d3_Rgb(r, g, b); + } + function d3_Rgb(r, g, b) { + this.r = r; + this.g = g; + this.b = b; + } + var d3_rgbPrototype = d3_Rgb.prototype = new d3_Color(); + d3_rgbPrototype.brighter = function(k) { + k = Math.pow(.7, arguments.length ? k : 1); + var r = this.r, g = this.g, b = this.b, i = 30; + if (!r && !g && !b) return d3_rgb(i, i, i); + if (r && r < i) r = i; + if (g && g < i) g = i; + if (b && b < i) b = i; + return d3_rgb(Math.min(255, Math.floor(r / k)), Math.min(255, Math.floor(g / k)), Math.min(255, Math.floor(b / k))); + }; + d3_rgbPrototype.darker = function(k) { + k = Math.pow(.7, arguments.length ? k : 1); + return d3_rgb(Math.floor(k * this.r), Math.floor(k * this.g), Math.floor(k * this.b)); + }; + d3_rgbPrototype.hsl = function() { + return d3_rgb_hsl(this.r, this.g, this.b); + }; + d3_rgbPrototype.toString = function() { + return "#" + d3_rgb_hex(this.r) + d3_rgb_hex(this.g) + d3_rgb_hex(this.b); + }; + function d3_rgb_hex(v) { + return v < 16 ? "0" + Math.max(0, v).toString(16) : Math.min(255, v).toString(16); + } + function d3_rgb_parse(format, rgb, hsl) { + var r = 0, g = 0, b = 0, m1, m2, name; + m1 = /([a-z]+)\((.*)\)/i.exec(format); + if (m1) { + m2 = m1[2].split(","); + switch (m1[1]) { + case "hsl": + { + return hsl(parseFloat(m2[0]), parseFloat(m2[1]) / 100, parseFloat(m2[2]) / 100); + } + + case "rgb": + { + return rgb(d3_rgb_parseNumber(m2[0]), d3_rgb_parseNumber(m2[1]), d3_rgb_parseNumber(m2[2])); + } + } + } + if (name = d3_rgb_names.get(format)) return rgb(name.r, name.g, name.b); + if (format != null && format.charAt(0) === "#") { + if (format.length === 4) { + r = format.charAt(1); + r += r; + g = format.charAt(2); + g += g; + b = format.charAt(3); + b += b; + } else if (format.length === 7) { + r = format.substring(1, 3); + g = format.substring(3, 5); + b = format.substring(5, 7); + } + r = parseInt(r, 16); + g = parseInt(g, 16); + b = parseInt(b, 16); + } + return rgb(r, g, b); + } + function d3_rgb_hsl(r, g, b) { + var min = Math.min(r /= 255, g /= 255, b /= 255), max = Math.max(r, g, b), d = max - min, h, s, l = (max + min) / 2; + if (d) { + s = l < .5 ? d / (max + min) : d / (2 - max - min); + if (r == max) h = (g - b) / d + (g < b ? 6 : 0); else if (g == max) h = (b - r) / d + 2; else h = (r - g) / d + 4; + h *= 60; + } else { + s = h = 0; + } + return d3_hsl(h, s, l); + } + function d3_rgb_lab(r, g, b) { + r = d3_rgb_xyz(r); + g = d3_rgb_xyz(g); + b = d3_rgb_xyz(b); + var x = d3_xyz_lab((.4124564 * r + .3575761 * g + .1804375 * b) / d3_lab_X), y = d3_xyz_lab((.2126729 * r + .7151522 * g + .072175 * b) / d3_lab_Y), z = d3_xyz_lab((.0193339 * r + .119192 * g + .9503041 * b) / d3_lab_Z); + return d3_lab(116 * y - 16, 500 * (x - y), 200 * (y - z)); + } + function d3_rgb_xyz(r) { + return (r /= 255) <= .04045 ? r / 12.92 : Math.pow((r + .055) / 1.055, 2.4); + } + function d3_rgb_parseNumber(c) { + var f = parseFloat(c); + return c.charAt(c.length - 1) === "%" ? Math.round(f * 2.55) : f; + } + var d3_rgb_names = d3.map({ + aliceblue: "#f0f8ff", + antiquewhite: "#faebd7", + aqua: "#00ffff", + aquamarine: "#7fffd4", + azure: "#f0ffff", + beige: "#f5f5dc", + bisque: "#ffe4c4", + black: "#000000", + blanchedalmond: "#ffebcd", + blue: "#0000ff", + blueviolet: "#8a2be2", + brown: "#a52a2a", + burlywood: "#deb887", + cadetblue: "#5f9ea0", + chartreuse: "#7fff00", + chocolate: "#d2691e", + coral: "#ff7f50", + cornflowerblue: "#6495ed", + cornsilk: "#fff8dc", + crimson: "#dc143c", + cyan: "#00ffff", + darkblue: "#00008b", + darkcyan: "#008b8b", + darkgoldenrod: "#b8860b", + darkgray: "#a9a9a9", + darkgreen: "#006400", + darkgrey: "#a9a9a9", + darkkhaki: "#bdb76b", + darkmagenta: "#8b008b", + darkolivegreen: "#556b2f", + darkorange: "#ff8c00", + darkorchid: "#9932cc", + darkred: "#8b0000", + darksalmon: "#e9967a", + darkseagreen: "#8fbc8f", + darkslateblue: "#483d8b", + darkslategray: "#2f4f4f", + darkslategrey: "#2f4f4f", + darkturquoise: "#00ced1", + darkviolet: "#9400d3", + deeppink: "#ff1493", + deepskyblue: "#00bfff", + dimgray: "#696969", + dimgrey: "#696969", + dodgerblue: "#1e90ff", + firebrick: "#b22222", + floralwhite: "#fffaf0", + forestgreen: "#228b22", + fuchsia: "#ff00ff", + gainsboro: "#dcdcdc", + ghostwhite: "#f8f8ff", + gold: "#ffd700", + goldenrod: "#daa520", + gray: "#808080", + green: "#008000", + greenyellow: "#adff2f", + grey: "#808080", + honeydew: "#f0fff0", + hotpink: "#ff69b4", + indianred: "#cd5c5c", + indigo: "#4b0082", + ivory: "#fffff0", + khaki: "#f0e68c", + lavender: "#e6e6fa", + lavenderblush: "#fff0f5", + lawngreen: "#7cfc00", + lemonchiffon: "#fffacd", + lightblue: "#add8e6", + lightcoral: "#f08080", + lightcyan: "#e0ffff", + lightgoldenrodyellow: "#fafad2", + lightgray: "#d3d3d3", + lightgreen: "#90ee90", + lightgrey: "#d3d3d3", + lightpink: "#ffb6c1", + lightsalmon: "#ffa07a", + lightseagreen: "#20b2aa", + lightskyblue: "#87cefa", + lightslategray: "#778899", + lightslategrey: "#778899", + lightsteelblue: "#b0c4de", + lightyellow: "#ffffe0", + lime: "#00ff00", + limegreen: "#32cd32", + linen: "#faf0e6", + magenta: "#ff00ff", + maroon: "#800000", + mediumaquamarine: "#66cdaa", + mediumblue: "#0000cd", + mediumorchid: "#ba55d3", + mediumpurple: "#9370db", + mediumseagreen: "#3cb371", + mediumslateblue: "#7b68ee", + mediumspringgreen: "#00fa9a", + mediumturquoise: "#48d1cc", + mediumvioletred: "#c71585", + midnightblue: "#191970", + mintcream: "#f5fffa", + mistyrose: "#ffe4e1", + moccasin: "#ffe4b5", + navajowhite: "#ffdead", + navy: "#000080", + oldlace: "#fdf5e6", + olive: "#808000", + olivedrab: "#6b8e23", + orange: "#ffa500", + orangered: "#ff4500", + orchid: "#da70d6", + palegoldenrod: "#eee8aa", + palegreen: "#98fb98", + paleturquoise: "#afeeee", + palevioletred: "#db7093", + papayawhip: "#ffefd5", + peachpuff: "#ffdab9", + peru: "#cd853f", + pink: "#ffc0cb", + plum: "#dda0dd", + powderblue: "#b0e0e6", + purple: "#800080", + red: "#ff0000", + rosybrown: "#bc8f8f", + royalblue: "#4169e1", + saddlebrown: "#8b4513", + salmon: "#fa8072", + sandybrown: "#f4a460", + seagreen: "#2e8b57", + seashell: "#fff5ee", + sienna: "#a0522d", + silver: "#c0c0c0", + skyblue: "#87ceeb", + slateblue: "#6a5acd", + slategray: "#708090", + slategrey: "#708090", + snow: "#fffafa", + springgreen: "#00ff7f", + steelblue: "#4682b4", + tan: "#d2b48c", + teal: "#008080", + thistle: "#d8bfd8", + tomato: "#ff6347", + turquoise: "#40e0d0", + violet: "#ee82ee", + wheat: "#f5deb3", + white: "#ffffff", + whitesmoke: "#f5f5f5", + yellow: "#ffff00", + yellowgreen: "#9acd32" + }); + d3_rgb_names.forEach(function(key, value) { + d3_rgb_names.set(key, d3_rgb_parse(value, d3_rgb, d3_hsl_rgb)); + }); + d3.hsl = function(h, s, l) { + return arguments.length === 1 ? h instanceof d3_Hsl ? d3_hsl(h.h, h.s, h.l) : d3_rgb_parse("" + h, d3_rgb_hsl, d3_hsl) : d3_hsl(+h, +s, +l); + }; + function d3_hsl(h, s, l) { + return new d3_Hsl(h, s, l); + } + function d3_Hsl(h, s, l) { + this.h = h; + this.s = s; + this.l = l; + } + var d3_hslPrototype = d3_Hsl.prototype = new d3_Color(); + d3_hslPrototype.brighter = function(k) { + k = Math.pow(.7, arguments.length ? k : 1); + return d3_hsl(this.h, this.s, this.l / k); + }; + d3_hslPrototype.darker = function(k) { + k = Math.pow(.7, arguments.length ? k : 1); + return d3_hsl(this.h, this.s, k * this.l); + }; + d3_hslPrototype.rgb = function() { + return d3_hsl_rgb(this.h, this.s, this.l); + }; + function d3_hsl_rgb(h, s, l) { + var m1, m2; + h = h % 360; + if (h < 0) h += 360; + s = s < 0 ? 0 : s > 1 ? 1 : s; + l = l < 0 ? 0 : l > 1 ? 1 : l; + m2 = l <= .5 ? l * (1 + s) : l + s - l * s; + m1 = 2 * l - m2; + function v(h) { + if (h > 360) h -= 360; else if (h < 0) h += 360; + if (h < 60) return m1 + (m2 - m1) * h / 60; + if (h < 180) return m2; + if (h < 240) return m1 + (m2 - m1) * (240 - h) / 60; + return m1; + } + function vv(h) { + return Math.round(v(h) * 255); + } + return d3_rgb(vv(h + 120), vv(h), vv(h - 120)); + } + d3.hcl = function(h, c, l) { + return arguments.length === 1 ? h instanceof d3_Hcl ? d3_hcl(h.h, h.c, h.l) : h instanceof d3_Lab ? d3_lab_hcl(h.l, h.a, h.b) : d3_lab_hcl((h = d3_rgb_lab((h = d3.rgb(h)).r, h.g, h.b)).l, h.a, h.b) : d3_hcl(+h, +c, +l); + }; + function d3_hcl(h, c, l) { + return new d3_Hcl(h, c, l); + } + function d3_Hcl(h, c, l) { + this.h = h; + this.c = c; + this.l = l; + } + var d3_hclPrototype = d3_Hcl.prototype = new d3_Color(); + d3_hclPrototype.brighter = function(k) { + return d3_hcl(this.h, this.c, Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1))); + }; + d3_hclPrototype.darker = function(k) { + return d3_hcl(this.h, this.c, Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1))); + }; + d3_hclPrototype.rgb = function() { + return d3_hcl_lab(this.h, this.c, this.l).rgb(); + }; + function d3_hcl_lab(h, c, l) { + return d3_lab(l, Math.cos(h *= d3_radians) * c, Math.sin(h) * c); + } + d3.lab = function(l, a, b) { + return arguments.length === 1 ? l instanceof d3_Lab ? d3_lab(l.l, l.a, l.b) : l instanceof d3_Hcl ? d3_hcl_lab(l.l, l.c, l.h) : d3_rgb_lab((l = d3.rgb(l)).r, l.g, l.b) : d3_lab(+l, +a, +b); + }; + function d3_lab(l, a, b) { + return new d3_Lab(l, a, b); + } + function d3_Lab(l, a, b) { + this.l = l; + this.a = a; + this.b = b; + } + var d3_lab_K = 18; + var d3_lab_X = .95047, d3_lab_Y = 1, d3_lab_Z = 1.08883; + var d3_labPrototype = d3_Lab.prototype = new d3_Color(); + d3_labPrototype.brighter = function(k) { + return d3_lab(Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)), this.a, this.b); + }; + d3_labPrototype.darker = function(k) { + return d3_lab(Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)), this.a, this.b); + }; + d3_labPrototype.rgb = function() { + return d3_lab_rgb(this.l, this.a, this.b); + }; + function d3_lab_rgb(l, a, b) { + var y = (l + 16) / 116, x = y + a / 500, z = y - b / 200; + x = d3_lab_xyz(x) * d3_lab_X; + y = d3_lab_xyz(y) * d3_lab_Y; + z = d3_lab_xyz(z) * d3_lab_Z; + return d3_rgb(d3_xyz_rgb(3.2404542 * x - 1.5371385 * y - .4985314 * z), d3_xyz_rgb(-.969266 * x + 1.8760108 * y + .041556 * z), d3_xyz_rgb(.0556434 * x - .2040259 * y + 1.0572252 * z)); + } + function d3_lab_hcl(l, a, b) { + return d3_hcl(Math.atan2(b, a) / π * 180, Math.sqrt(a * a + b * b), l); + } + function d3_lab_xyz(x) { + return x > .206893034 ? x * x * x : (x - 4 / 29) / 7.787037; + } + function d3_xyz_lab(x) { + return x > .008856 ? Math.pow(x, 1 / 3) : 7.787037 * x + 4 / 29; + } + function d3_xyz_rgb(r) { + return Math.round(255 * (r <= .00304 ? 12.92 * r : 1.055 * Math.pow(r, 1 / 2.4) - .055)); + } + function d3_selection(groups) { + d3_arraySubclass(groups, d3_selectionPrototype); + return groups; + } + var d3_select = function(s, n) { + return n.querySelector(s); + }, d3_selectAll = function(s, n) { + return n.querySelectorAll(s); + }, d3_selectRoot = d3_document.documentElement, d3_selectMatcher = d3_selectRoot.matchesSelector || d3_selectRoot.webkitMatchesSelector || d3_selectRoot.mozMatchesSelector || d3_selectRoot.msMatchesSelector || d3_selectRoot.oMatchesSelector, d3_selectMatches = function(n, s) { + return d3_selectMatcher.call(n, s); + }; + if (typeof Sizzle === "function") { + d3_select = function(s, n) { + return Sizzle(s, n)[0] || null; + }; + d3_selectAll = function(s, n) { + return Sizzle.uniqueSort(Sizzle(s, n)); + }; + d3_selectMatches = Sizzle.matchesSelector; + } + var d3_selectionPrototype = []; + d3.selection = function() { + return d3_selectionRoot; + }; + d3.selection.prototype = d3_selectionPrototype; + d3_selectionPrototype.select = function(selector) { + var subgroups = [], subgroup, subnode, group, node; + if (typeof selector !== "function") selector = d3_selection_selector(selector); + for (var j = -1, m = this.length; ++j < m; ) { + subgroups.push(subgroup = []); + subgroup.parentNode = (group = this[j]).parentNode; + for (var i = -1, n = group.length; ++i < n; ) { + if (node = group[i]) { + subgroup.push(subnode = selector.call(node, node.__data__, i)); + if (subnode && "__data__" in node) subnode.__data__ = node.__data__; + } else { + subgroup.push(null); + } + } + } + return d3_selection(subgroups); + }; + function d3_selection_selector(selector) { + return function() { + return d3_select(selector, this); + }; + } + d3_selectionPrototype.selectAll = function(selector) { + var subgroups = [], subgroup, node; + if (typeof selector !== "function") selector = d3_selection_selectorAll(selector); + for (var j = -1, m = this.length; ++j < m; ) { + for (var group = this[j], i = -1, n = group.length; ++i < n; ) { + if (node = group[i]) { + subgroups.push(subgroup = d3_array(selector.call(node, node.__data__, i))); + subgroup.parentNode = node; + } + } + } + return d3_selection(subgroups); + }; + function d3_selection_selectorAll(selector) { + return function() { + return d3_selectAll(selector, this); + }; + } + d3_selectionPrototype.attr = function(name, value) { + if (arguments.length < 2) { + if (typeof name === "string") { + var node = this.node(); + name = d3.ns.qualify(name); + return name.local ? node.getAttributeNS(name.space, name.local) : node.getAttribute(name); + } + for (value in name) this.each(d3_selection_attr(value, name[value])); + return this; + } + return this.each(d3_selection_attr(name, value)); + }; + function d3_selection_attr(name, value) { + name = d3.ns.qualify(name); + function attrNull() { + this.removeAttribute(name); + } + function attrNullNS() { + this.removeAttributeNS(name.space, name.local); + } + function attrConstant() { + this.setAttribute(name, value); + } + function attrConstantNS() { + this.setAttributeNS(name.space, name.local, value); + } + function attrFunction() { + var x = value.apply(this, arguments); + if (x == null) this.removeAttribute(name); else this.setAttribute(name, x); + } + function attrFunctionNS() { + var x = value.apply(this, arguments); + if (x == null) this.removeAttributeNS(name.space, name.local); else this.setAttributeNS(name.space, name.local, x); + } + return value == null ? name.local ? attrNullNS : attrNull : typeof value === "function" ? name.local ? attrFunctionNS : attrFunction : name.local ? attrConstantNS : attrConstant; + } + d3_selectionPrototype.classed = function(name, value) { + if (arguments.length < 2) { + if (typeof name === "string") { + var node = this.node(), n = (name = name.trim().split(/^|\s+/g)).length, i = -1; + if (value = node.classList) { + while (++i < n) if (!value.contains(name[i])) return false; + } else { + value = node.className; + if (value.baseVal != null) value = value.baseVal; + while (++i < n) if (!d3_selection_classedRe(name[i]).test(value)) return false; + } + return true; + } + for (value in name) this.each(d3_selection_classed(value, name[value])); + return this; + } + return this.each(d3_selection_classed(name, value)); + }; + function d3_selection_classedRe(name) { + return new RegExp("(?:^|\\s+)" + d3.requote(name) + "(?:\\s+|$)", "g"); + } + function d3_selection_classed(name, value) { + name = name.trim().split(/\s+/).map(d3_selection_classedName); + var n = name.length; + function classedConstant() { + var i = -1; + while (++i < n) name[i](this, value); + } + function classedFunction() { + var i = -1, x = value.apply(this, arguments); + while (++i < n) name[i](this, x); + } + return typeof value === "function" ? classedFunction : classedConstant; + } + function d3_selection_classedName(name) { + var re = d3_selection_classedRe(name); + return function(node, value) { + if (c = node.classList) return value ? c.add(name) : c.remove(name); + var c = node.className, cb = c.baseVal != null, cv = cb ? c.baseVal : c; + if (value) { + re.lastIndex = 0; + if (!re.test(cv)) { + cv = d3_collapse(cv + " " + name); + if (cb) c.baseVal = cv; else node.className = cv; + } + } else if (cv) { + cv = d3_collapse(cv.replace(re, " ")); + if (cb) c.baseVal = cv; else node.className = cv; + } + }; + } + d3_selectionPrototype.style = function(name, value, priority) { + var n = arguments.length; + if (n < 3) { + if (typeof name !== "string") { + if (n < 2) value = ""; + for (priority in name) this.each(d3_selection_style(priority, name[priority], value)); + return this; + } + if (n < 2) return d3_window.getComputedStyle(this.node(), null).getPropertyValue(name); + priority = ""; + } + return this.each(d3_selection_style(name, value, priority)); + }; + function d3_selection_style(name, value, priority) { + function styleNull() { + this.style.removeProperty(name); + } + function styleConstant() { + this.style.setProperty(name, value, priority); + } + function styleFunction() { + var x = value.apply(this, arguments); + if (x == null) this.style.removeProperty(name); else this.style.setProperty(name, x, priority); + } + return value == null ? styleNull : typeof value === "function" ? styleFunction : styleConstant; + } + d3_selectionPrototype.property = function(name, value) { + if (arguments.length < 2) { + if (typeof name === "string") return this.node()[name]; + for (value in name) this.each(d3_selection_property(value, name[value])); + return this; + } + return this.each(d3_selection_property(name, value)); + }; + function d3_selection_property(name, value) { + function propertyNull() { + delete this[name]; + } + function propertyConstant() { + this[name] = value; + } + function propertyFunction() { + var x = value.apply(this, arguments); + if (x == null) delete this[name]; else this[name] = x; + } + return value == null ? propertyNull : typeof value === "function" ? propertyFunction : propertyConstant; + } + d3_selectionPrototype.text = function(value) { + return arguments.length ? this.each(typeof value === "function" ? function() { + var v = value.apply(this, arguments); + this.textContent = v == null ? "" : v; + } : value == null ? function() { + this.textContent = ""; + } : function() { + this.textContent = value; + }) : this.node().textContent; + }; + d3_selectionPrototype.html = function(value) { + return arguments.length ? this.each(typeof value === "function" ? function() { + var v = value.apply(this, arguments); + this.innerHTML = v == null ? "" : v; + } : value == null ? function() { + this.innerHTML = ""; + } : function() { + this.innerHTML = value; + }) : this.node().innerHTML; + }; + d3_selectionPrototype.append = function(name) { + name = d3.ns.qualify(name); + function append() { + return this.appendChild(d3_document.createElementNS(this.namespaceURI, name)); + } + function appendNS() { + return this.appendChild(d3_document.createElementNS(name.space, name.local)); + } + return this.select(name.local ? appendNS : append); + }; + d3_selectionPrototype.insert = function(name, before) { + name = d3.ns.qualify(name); + function insert() { + return this.insertBefore(d3_document.createElementNS(this.namespaceURI, name), d3_select(before, this)); + } + function insertNS() { + return this.insertBefore(d3_document.createElementNS(name.space, name.local), d3_select(before, this)); + } + return this.select(name.local ? insertNS : insert); + }; + d3_selectionPrototype.remove = function() { + return this.each(function() { + var parent = this.parentNode; + if (parent) parent.removeChild(this); + }); + }; + d3_selectionPrototype.data = function(value, key) { + var i = -1, n = this.length, group, node; + if (!arguments.length) { + value = new Array(n = (group = this[0]).length); + while (++i < n) { + if (node = group[i]) { + value[i] = node.__data__; + } + } + return value; + } + function bind(group, groupData) { + var i, n = group.length, m = groupData.length, n0 = Math.min(n, m), updateNodes = new Array(m), enterNodes = new Array(m), exitNodes = new Array(n), node, nodeData; + if (key) { + var nodeByKeyValue = new d3_Map(), dataByKeyValue = new d3_Map(), keyValues = [], keyValue; + for (i = -1; ++i < n; ) { + keyValue = key.call(node = group[i], node.__data__, i); + if (nodeByKeyValue.has(keyValue)) { + exitNodes[i] = node; + } else { + nodeByKeyValue.set(keyValue, node); + } + keyValues.push(keyValue); + } + for (i = -1; ++i < m; ) { + keyValue = key.call(groupData, nodeData = groupData[i], i); + if (node = nodeByKeyValue.get(keyValue)) { + updateNodes[i] = node; + node.__data__ = nodeData; + } else if (!dataByKeyValue.has(keyValue)) { + enterNodes[i] = d3_selection_dataNode(nodeData); + } + dataByKeyValue.set(keyValue, nodeData); + nodeByKeyValue.remove(keyValue); + } + for (i = -1; ++i < n; ) { + if (nodeByKeyValue.has(keyValues[i])) { + exitNodes[i] = group[i]; + } + } + } else { + for (i = -1; ++i < n0; ) { + node = group[i]; + nodeData = groupData[i]; + if (node) { + node.__data__ = nodeData; + updateNodes[i] = node; + } else { + enterNodes[i] = d3_selection_dataNode(nodeData); + } + } + for (;i < m; ++i) { + enterNodes[i] = d3_selection_dataNode(groupData[i]); + } + for (;i < n; ++i) { + exitNodes[i] = group[i]; + } + } + enterNodes.update = updateNodes; + enterNodes.parentNode = updateNodes.parentNode = exitNodes.parentNode = group.parentNode; + enter.push(enterNodes); + update.push(updateNodes); + exit.push(exitNodes); + } + var enter = d3_selection_enter([]), update = d3_selection([]), exit = d3_selection([]); + if (typeof value === "function") { + while (++i < n) { + bind(group = this[i], value.call(group, group.parentNode.__data__, i)); + } + } else { + while (++i < n) { + bind(group = this[i], value); + } + } + update.enter = function() { + return enter; + }; + update.exit = function() { + return exit; + }; + return update; + }; + function d3_selection_dataNode(data) { + return { + __data__: data + }; + } + d3_selectionPrototype.datum = function(value) { + return arguments.length ? this.property("__data__", value) : this.property("__data__"); + }; + d3_selectionPrototype.filter = function(filter) { + var subgroups = [], subgroup, group, node; + if (typeof filter !== "function") filter = d3_selection_filter(filter); + for (var j = 0, m = this.length; j < m; j++) { + subgroups.push(subgroup = []); + subgroup.parentNode = (group = this[j]).parentNode; + for (var i = 0, n = group.length; i < n; i++) { + if ((node = group[i]) && filter.call(node, node.__data__, i)) { + subgroup.push(node); + } + } + } + return d3_selection(subgroups); + }; + function d3_selection_filter(selector) { + return function() { + return d3_selectMatches(this, selector); + }; + } + d3_selectionPrototype.order = function() { + for (var j = -1, m = this.length; ++j < m; ) { + for (var group = this[j], i = group.length - 1, next = group[i], node; --i >= 0; ) { + if (node = group[i]) { + if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next); + next = node; + } + } + } + return this; + }; + d3_selectionPrototype.sort = function(comparator) { + comparator = d3_selection_sortComparator.apply(this, arguments); + for (var j = -1, m = this.length; ++j < m; ) this[j].sort(comparator); + return this.order(); + }; + function d3_selection_sortComparator(comparator) { + if (!arguments.length) comparator = d3.ascending; + return function(a, b) { + return !a - !b || comparator(a.__data__, b.__data__); + }; + } + d3_selectionPrototype.on = function(type, listener, capture) { + var n = arguments.length; + if (n < 3) { + if (typeof type !== "string") { + if (n < 2) listener = false; + for (capture in type) this.each(d3_selection_on(capture, type[capture], listener)); + return this; + } + if (n < 2) return (n = this.node()["__on" + type]) && n._; + capture = false; + } + return this.each(d3_selection_on(type, listener, capture)); + }; + function d3_selection_on(type, listener, capture) { + var name = "__on" + type, i = type.indexOf("."); + if (i > 0) type = type.substring(0, i); + function onRemove() { + var wrapper = this[name]; + if (wrapper) { + this.removeEventListener(type, wrapper, wrapper.$); + delete this[name]; + } + } + function onAdd() { + var node = this, args = d3_array(arguments); + onRemove.call(this); + this.addEventListener(type, this[name] = wrapper, wrapper.$ = capture); + wrapper._ = listener; + function wrapper(e) { + var o = d3.event; + d3.event = e; + args[0] = node.__data__; + try { + listener.apply(node, args); + } finally { + d3.event = o; + } + } + } + return listener ? onAdd : onRemove; + } + d3_selectionPrototype.each = function(callback) { + return d3_selection_each(this, function(node, i, j) { + callback.call(node, node.__data__, i, j); + }); + }; + function d3_selection_each(groups, callback) { + for (var j = 0, m = groups.length; j < m; j++) { + for (var group = groups[j], i = 0, n = group.length, node; i < n; i++) { + if (node = group[i]) callback(node, i, j); + } + } + return groups; + } + d3_selectionPrototype.call = function(callback) { + var args = d3_array(arguments); + callback.apply(args[0] = this, args); + return this; + }; + d3_selectionPrototype.empty = function() { + return !this.node(); + }; + d3_selectionPrototype.node = function() { + for (var j = 0, m = this.length; j < m; j++) { + for (var group = this[j], i = 0, n = group.length; i < n; i++) { + var node = group[i]; + if (node) return node; + } + } + return null; + }; + d3_selectionPrototype.transition = function() { + var id = d3_transitionInheritId || ++d3_transitionId, subgroups = [], subgroup, node, transition = Object.create(d3_transitionInherit); + transition.time = Date.now(); + for (var j = -1, m = this.length; ++j < m; ) { + subgroups.push(subgroup = []); + for (var group = this[j], i = -1, n = group.length; ++i < n; ) { + if (node = group[i]) d3_transitionNode(node, i, id, transition); + subgroup.push(node); + } + } + return d3_transition(subgroups, id); + }; + var d3_selectionRoot = d3_selection([ [ d3_document ] ]); + d3_selectionRoot[0].parentNode = d3_selectRoot; + d3.select = function(selector) { + return typeof selector === "string" ? d3_selectionRoot.select(selector) : d3_selection([ [ selector ] ]); + }; + d3.selectAll = function(selector) { + return typeof selector === "string" ? d3_selectionRoot.selectAll(selector) : d3_selection([ d3_array(selector) ]); + }; + function d3_selection_enter(selection) { + d3_arraySubclass(selection, d3_selection_enterPrototype); + return selection; + } + var d3_selection_enterPrototype = []; + d3.selection.enter = d3_selection_enter; + d3.selection.enter.prototype = d3_selection_enterPrototype; + d3_selection_enterPrototype.append = d3_selectionPrototype.append; + d3_selection_enterPrototype.insert = d3_selectionPrototype.insert; + d3_selection_enterPrototype.empty = d3_selectionPrototype.empty; + d3_selection_enterPrototype.node = d3_selectionPrototype.node; + d3_selection_enterPrototype.select = function(selector) { + var subgroups = [], subgroup, subnode, upgroup, group, node; + for (var j = -1, m = this.length; ++j < m; ) { + upgroup = (group = this[j]).update; + subgroups.push(subgroup = []); + subgroup.parentNode = group.parentNode; + for (var i = -1, n = group.length; ++i < n; ) { + if (node = group[i]) { + subgroup.push(upgroup[i] = subnode = selector.call(group.parentNode, node.__data__, i)); + subnode.__data__ = node.__data__; + } else { + subgroup.push(null); + } + } + } + return d3_selection(subgroups); + }; + function d3_transition(groups, id) { + d3_arraySubclass(groups, d3_transitionPrototype); + groups.id = id; + return groups; + } + var d3_transitionPrototype = [], d3_transitionId = 0, d3_transitionInheritId, d3_transitionInherit = { + ease: d3_ease_cubicInOut, + delay: 0, + duration: 250 + }; + d3_transitionPrototype.call = d3_selectionPrototype.call; + d3_transitionPrototype.empty = d3_selectionPrototype.empty; + d3_transitionPrototype.node = d3_selectionPrototype.node; + d3.transition = function(selection) { + return arguments.length ? d3_transitionInheritId ? selection.transition() : selection : d3_selectionRoot.transition(); + }; + d3.transition.prototype = d3_transitionPrototype; + function d3_transitionNode(node, i, id, inherit) { + var lock = node.__transition__ || (node.__transition__ = { + active: 0, + count: 0 + }), transition = lock[id]; + if (!transition) { + var time = inherit.time; + transition = lock[id] = { + tween: new d3_Map(), + event: d3.dispatch("start", "end"), + time: time, + ease: inherit.ease, + delay: inherit.delay, + duration: inherit.duration + }; + ++lock.count; + d3.timer(function(elapsed) { + var d = node.__data__, ease = transition.ease, event = transition.event, delay = transition.delay, duration = transition.duration, tweened = []; + return delay <= elapsed ? start(elapsed) : d3.timer(start, delay, time), 1; + function start(elapsed) { + if (lock.active > id) return stop(); + lock.active = id; + event.start.call(node, d, i); + transition.tween.forEach(function(key, value) { + if (value = value.call(node, d, i)) { + tweened.push(value); + } + }); + if (!tick(elapsed)) d3.timer(tick, 0, time); + return 1; + } + function tick(elapsed) { + if (lock.active !== id) return stop(); + var t = (elapsed - delay) / duration, e = ease(t), n = tweened.length; + while (n > 0) { + tweened[--n].call(node, e); + } + if (t >= 1) { + stop(); + event.end.call(node, d, i); + return 1; + } + } + function stop() { + if (--lock.count) delete lock[id]; else delete node.__transition__; + return 1; + } + }, 0, time); + return transition; + } + } + d3_transitionPrototype.select = function(selector) { + var id = this.id, subgroups = [], subgroup, subnode, node; + if (typeof selector !== "function") selector = d3_selection_selector(selector); + for (var j = -1, m = this.length; ++j < m; ) { + subgroups.push(subgroup = []); + for (var group = this[j], i = -1, n = group.length; ++i < n; ) { + if ((node = group[i]) && (subnode = selector.call(node, node.__data__, i))) { + if ("__data__" in node) subnode.__data__ = node.__data__; + d3_transitionNode(subnode, i, id, node.__transition__[id]); + subgroup.push(subnode); + } else { + subgroup.push(null); + } + } + } + return d3_transition(subgroups, id); + }; + d3_transitionPrototype.selectAll = function(selector) { + var id = this.id, subgroups = [], subgroup, subnodes, node, subnode, transition; + if (typeof selector !== "function") selector = d3_selection_selectorAll(selector); + for (var j = -1, m = this.length; ++j < m; ) { + for (var group = this[j], i = -1, n = group.length; ++i < n; ) { + if (node = group[i]) { + transition = node.__transition__[id]; + subnodes = selector.call(node, node.__data__, i); + subgroups.push(subgroup = []); + for (var k = -1, o = subnodes.length; ++k < o; ) { + d3_transitionNode(subnode = subnodes[k], k, id, transition); + subgroup.push(subnode); + } + } + } + } + return d3_transition(subgroups, id); + }; + d3_transitionPrototype.filter = function(filter) { + var subgroups = [], subgroup, group, node; + if (typeof filter !== "function") filter = d3_selection_filter(filter); + for (var j = 0, m = this.length; j < m; j++) { + subgroups.push(subgroup = []); + for (var group = this[j], i = 0, n = group.length; i < n; i++) { + if ((node = group[i]) && filter.call(node, node.__data__, i)) { + subgroup.push(node); + } + } + } + return d3_transition(subgroups, this.id, this.time).ease(this.ease()); + }; + d3_transitionPrototype.attr = function(nameNS, value) { + if (arguments.length < 2) { + for (value in nameNS) this.attr(value, nameNS[value]); + return this; + } + var interpolate = d3_interpolateByName(nameNS), name = d3.ns.qualify(nameNS); + function attrNull() { + this.removeAttribute(name); + } + function attrNullNS() { + this.removeAttributeNS(name.space, name.local); + } + return d3_transition_tween(this, "attr." + nameNS, value, function(b) { + function attrString() { + var a = this.getAttribute(name), i; + return a !== b && (i = interpolate(a, b), function(t) { + this.setAttribute(name, i(t)); + }); + } + function attrStringNS() { + var a = this.getAttributeNS(name.space, name.local), i; + return a !== b && (i = interpolate(a, b), function(t) { + this.setAttributeNS(name.space, name.local, i(t)); + }); + } + return b == null ? name.local ? attrNullNS : attrNull : (b += "", name.local ? attrStringNS : attrString); + }); + }; + d3_transitionPrototype.attrTween = function(nameNS, tween) { + var name = d3.ns.qualify(nameNS); + function attrTween(d, i) { + var f = tween.call(this, d, i, this.getAttribute(name)); + return f && function(t) { + this.setAttribute(name, f(t)); + }; + } + function attrTweenNS(d, i) { + var f = tween.call(this, d, i, this.getAttributeNS(name.space, name.local)); + return f && function(t) { + this.setAttributeNS(name.space, name.local, f(t)); + }; + } + return this.tween("attr." + nameNS, name.local ? attrTweenNS : attrTween); + }; + d3_transitionPrototype.style = function(name, value, priority) { + var n = arguments.length; + if (n < 3) { + if (typeof name !== "string") { + if (n < 2) value = ""; + for (priority in name) this.style(priority, name[priority], value); + return this; + } + priority = ""; + } + var interpolate = d3_interpolateByName(name); + function styleNull() { + this.style.removeProperty(name); + } + return d3_transition_tween(this, "style." + name, value, function(b) { + function styleString() { + var a = d3_window.getComputedStyle(this, null).getPropertyValue(name), i; + return a !== b && (i = interpolate(a, b), function(t) { + this.style.setProperty(name, i(t), priority); + }); + } + return b == null ? styleNull : (b += "", styleString); + }); + }; + d3_transitionPrototype.styleTween = function(name, tween, priority) { + if (arguments.length < 3) priority = ""; + return this.tween("style." + name, function(d, i) { + var f = tween.call(this, d, i, d3_window.getComputedStyle(this, null).getPropertyValue(name)); + return f && function(t) { + this.style.setProperty(name, f(t), priority); + }; + }); + }; + d3_transitionPrototype.text = function(value) { + return d3_transition_tween(this, "text", value, d3_transition_text); + }; + function d3_transition_text(b) { + if (b == null) b = ""; + return function() { + this.textContent = b; + }; + } + d3_transitionPrototype.remove = function() { + return this.each("end.transition", function() { + var p; + if (!this.__transition__ && (p = this.parentNode)) p.removeChild(this); + }); + }; + d3_transitionPrototype.ease = function(value) { + var id = this.id; + if (arguments.length < 1) return this.node().__transition__[id].ease; + if (typeof value !== "function") value = d3.ease.apply(d3, arguments); + return d3_selection_each(this, function(node) { + node.__transition__[id].ease = value; + }); + }; + d3_transitionPrototype.delay = function(value) { + var id = this.id; + return d3_selection_each(this, typeof value === "function" ? function(node, i, j) { + node.__transition__[id].delay = value.call(node, node.__data__, i, j) | 0; + } : (value |= 0, function(node) { + node.__transition__[id].delay = value; + })); + }; + d3_transitionPrototype.duration = function(value) { + var id = this.id; + return d3_selection_each(this, typeof value === "function" ? function(node, i, j) { + node.__transition__[id].duration = Math.max(1, value.call(node, node.__data__, i, j) | 0); + } : (value = Math.max(1, value | 0), function(node) { + node.__transition__[id].duration = value; + })); + }; + d3_transitionPrototype.each = function(type, listener) { + var id = this.id; + if (arguments.length < 2) { + var inherit = d3_transitionInherit, inheritId = d3_transitionInheritId; + d3_transitionInheritId = id; + d3_selection_each(this, function(node, i, j) { + d3_transitionInherit = node.__transition__[id]; + type.call(node, node.__data__, i, j); + }); + d3_transitionInherit = inherit; + d3_transitionInheritId = inheritId; + } else { + d3_selection_each(this, function(node) { + node.__transition__[id].event.on(type, listener); + }); + } + return this; + }; + d3_transitionPrototype.transition = function() { + var id0 = this.id, id1 = ++d3_transitionId, subgroups = [], subgroup, group, node, transition; + for (var j = 0, m = this.length; j < m; j++) { + subgroups.push(subgroup = []); + for (var group = this[j], i = 0, n = group.length; i < n; i++) { + if (node = group[i]) { + transition = Object.create(node.__transition__[id0]); + transition.delay += transition.duration; + d3_transitionNode(node, i, id1, transition); + } + subgroup.push(node); + } + } + return d3_transition(subgroups, id1); + }; + d3_transitionPrototype.tween = function(name, tween) { + var id = this.id; + if (arguments.length < 2) return this.node().__transition__[id].tween.get(name); + return d3_selection_each(this, tween == null ? function(node) { + node.__transition__[id].tween.remove(name); + } : function(node) { + node.__transition__[id].tween.set(name, tween); + }); + }; + function d3_transition_tween(groups, name, value, tween) { + var id = groups.id; + return d3_selection_each(groups, typeof value === "function" ? function(node, i, j) { + node.__transition__[id].tween.set(name, tween(value.call(node, node.__data__, i, j))); + } : (value = tween(value), function(node) { + node.__transition__[id].tween.set(name, value); + })); + } + var d3_timer_id = 0, d3_timer_byId = {}, d3_timer_queue = null, d3_timer_interval, d3_timer_timeout; + d3.timer = function(callback, delay, then) { + if (arguments.length < 3) { + if (arguments.length < 2) delay = 0; else if (!isFinite(delay)) return; + then = Date.now(); + } + var timer = d3_timer_byId[callback.id]; + if (timer && timer.callback === callback) { + timer.then = then; + timer.delay = delay; + } else d3_timer_byId[callback.id = ++d3_timer_id] = d3_timer_queue = { + callback: callback, + then: then, + delay: delay, + next: d3_timer_queue + }; + if (!d3_timer_interval) { + d3_timer_timeout = clearTimeout(d3_timer_timeout); + d3_timer_interval = 1; + d3_timer_frame(d3_timer_step); + } + }; + function d3_timer_step() { + var elapsed, now = Date.now(), t1 = d3_timer_queue; + while (t1) { + elapsed = now - t1.then; + if (elapsed >= t1.delay) t1.flush = t1.callback(elapsed); + t1 = t1.next; + } + var delay = d3_timer_flush() - now; + if (delay > 24) { + if (isFinite(delay)) { + clearTimeout(d3_timer_timeout); + d3_timer_timeout = setTimeout(d3_timer_step, delay); + } + d3_timer_interval = 0; + } else { + d3_timer_interval = 1; + d3_timer_frame(d3_timer_step); + } + } + d3.timer.flush = function() { + var elapsed, now = Date.now(), t1 = d3_timer_queue; + while (t1) { + elapsed = now - t1.then; + if (!t1.delay) t1.flush = t1.callback(elapsed); + t1 = t1.next; + } + d3_timer_flush(); + }; + function d3_timer_flush() { + var t0 = null, t1 = d3_timer_queue, then = Infinity; + while (t1) { + if (t1.flush) { + delete d3_timer_byId[t1.callback.id]; + t1 = t0 ? t0.next = t1.next : d3_timer_queue = t1.next; + } else { + then = Math.min(then, t1.then + t1.delay); + t1 = (t0 = t1).next; + } + } + return then; + } + var d3_timer_frame = d3_window.requestAnimationFrame || d3_window.webkitRequestAnimationFrame || d3_window.mozRequestAnimationFrame || d3_window.oRequestAnimationFrame || d3_window.msRequestAnimationFrame || function(callback) { + setTimeout(callback, 17); + }; + d3.mouse = function(container) { + return d3_mousePoint(container, d3_eventSource()); + }; + var d3_mouse_bug44083 = /WebKit/.test(d3_window.navigator.userAgent) ? -1 : 0; + function d3_mousePoint(container, e) { + var svg = container.ownerSVGElement || container; + if (svg.createSVGPoint) { + var point = svg.createSVGPoint(); + if (d3_mouse_bug44083 < 0 && (d3_window.scrollX || d3_window.scrollY)) { + svg = d3.select(d3_document.body).append("svg").style("position", "absolute").style("top", 0).style("left", 0); + var ctm = svg[0][0].getScreenCTM(); + d3_mouse_bug44083 = !(ctm.f || ctm.e); + svg.remove(); + } + if (d3_mouse_bug44083) { + point.x = e.pageX; + point.y = e.pageY; + } else { + point.x = e.clientX; + point.y = e.clientY; + } + point = point.matrixTransform(container.getScreenCTM().inverse()); + return [ point.x, point.y ]; + } + var rect = container.getBoundingClientRect(); + return [ e.clientX - rect.left - container.clientLeft, e.clientY - rect.top - container.clientTop ]; + } + d3.touches = function(container, touches) { + if (arguments.length < 2) touches = d3_eventSource().touches; + return touches ? d3_array(touches).map(function(touch) { + var point = d3_mousePoint(container, touch); + point.identifier = touch.identifier; + return point; + }) : []; + }; + function d3_noop() {} + d3.scale = {}; + function d3_scaleExtent(domain) { + var start = domain[0], stop = domain[domain.length - 1]; + return start < stop ? [ start, stop ] : [ stop, start ]; + } + function d3_scaleRange(scale) { + return scale.rangeExtent ? scale.rangeExtent() : d3_scaleExtent(scale.range()); + } + function d3_scale_nice(domain, nice) { + var i0 = 0, i1 = domain.length - 1, x0 = domain[i0], x1 = domain[i1], dx; + if (x1 < x0) { + dx = i0, i0 = i1, i1 = dx; + dx = x0, x0 = x1, x1 = dx; + } + if (nice = nice(x1 - x0)) { + domain[i0] = nice.floor(x0); + domain[i1] = nice.ceil(x1); + } + return domain; + } + function d3_scale_niceDefault() { + return Math; + } + d3.scale.linear = function() { + return d3_scale_linear([ 0, 1 ], [ 0, 1 ], d3.interpolate, false); + }; + function d3_scale_linear(domain, range, interpolate, clamp) { + var output, input; + function rescale() { + var linear = Math.min(domain.length, range.length) > 2 ? d3_scale_polylinear : d3_scale_bilinear, uninterpolate = clamp ? d3_uninterpolateClamp : d3_uninterpolateNumber; + output = linear(domain, range, uninterpolate, interpolate); + input = linear(range, domain, uninterpolate, d3.interpolate); + return scale; + } + function scale(x) { + return output(x); + } + scale.invert = function(y) { + return input(y); + }; + scale.domain = function(x) { + if (!arguments.length) return domain; + domain = x.map(Number); + return rescale(); + }; + scale.range = function(x) { + if (!arguments.length) return range; + range = x; + return rescale(); + }; + scale.rangeRound = function(x) { + return scale.range(x).interpolate(d3.interpolateRound); + }; + scale.clamp = function(x) { + if (!arguments.length) return clamp; + clamp = x; + return rescale(); + }; + scale.interpolate = function(x) { + if (!arguments.length) return interpolate; + interpolate = x; + return rescale(); + }; + scale.ticks = function(m) { + return d3_scale_linearTicks(domain, m); + }; + scale.tickFormat = function(m) { + return d3_scale_linearTickFormat(domain, m); + }; + scale.nice = function() { + d3_scale_nice(domain, d3_scale_linearNice); + return rescale(); + }; + scale.copy = function() { + return d3_scale_linear(domain, range, interpolate, clamp); + }; + return rescale(); + } + function d3_scale_linearRebind(scale, linear) { + return d3.rebind(scale, linear, "range", "rangeRound", "interpolate", "clamp"); + } + function d3_scale_linearNice(dx) { + dx = Math.pow(10, Math.round(Math.log(dx) / Math.LN10) - 1); + return dx && { + floor: function(x) { + return Math.floor(x / dx) * dx; + }, + ceil: function(x) { + return Math.ceil(x / dx) * dx; + } + }; + } + function d3_scale_linearTickRange(domain, m) { + var extent = d3_scaleExtent(domain), span = extent[1] - extent[0], step = Math.pow(10, Math.floor(Math.log(span / m) / Math.LN10)), err = m / span * step; + if (err <= .15) step *= 10; else if (err <= .35) step *= 5; else if (err <= .75) step *= 2; + extent[0] = Math.ceil(extent[0] / step) * step; + extent[1] = Math.floor(extent[1] / step) * step + step * .5; + extent[2] = step; + return extent; + } + function d3_scale_linearTicks(domain, m) { + return d3.range.apply(d3, d3_scale_linearTickRange(domain, m)); + } + function d3_scale_linearTickFormat(domain, m) { + return d3.format(",." + Math.max(0, -Math.floor(Math.log(d3_scale_linearTickRange(domain, m)[2]) / Math.LN10 + .01)) + "f"); + } + function d3_scale_bilinear(domain, range, uninterpolate, interpolate) { + var u = uninterpolate(domain[0], domain[1]), i = interpolate(range[0], range[1]); + return function(x) { + return i(u(x)); + }; + } + function d3_scale_polylinear(domain, range, uninterpolate, interpolate) { + var u = [], i = [], j = 0, k = Math.min(domain.length, range.length) - 1; + if (domain[k] < domain[0]) { + domain = domain.slice().reverse(); + range = range.slice().reverse(); + } + while (++j <= k) { + u.push(uninterpolate(domain[j - 1], domain[j])); + i.push(interpolate(range[j - 1], range[j])); + } + return function(x) { + var j = d3.bisect(domain, x, 1, k) - 1; + return i[j](u[j](x)); + }; + } + d3.scale.log = function() { + return d3_scale_log(d3.scale.linear(), d3_scale_logp); + }; + function d3_scale_log(linear, log) { + var pow = log.pow; + function scale(x) { + return linear(log(x)); + } + scale.invert = function(x) { + return pow(linear.invert(x)); + }; + scale.domain = function(x) { + if (!arguments.length) return linear.domain().map(pow); + log = x[0] < 0 ? d3_scale_logn : d3_scale_logp; + pow = log.pow; + linear.domain(x.map(log)); + return scale; + }; + scale.nice = function() { + linear.domain(d3_scale_nice(linear.domain(), d3_scale_niceDefault)); + return scale; + }; + scale.ticks = function() { + var extent = d3_scaleExtent(linear.domain()), ticks = []; + if (extent.every(isFinite)) { + var i = Math.floor(extent[0]), j = Math.ceil(extent[1]), u = pow(extent[0]), v = pow(extent[1]); + if (log === d3_scale_logn) { + ticks.push(pow(i)); + for (;i++ < j; ) for (var k = 9; k > 0; k--) ticks.push(pow(i) * k); + } else { + for (;i < j; i++) for (var k = 1; k < 10; k++) ticks.push(pow(i) * k); + ticks.push(pow(i)); + } + for (i = 0; ticks[i] < u; i++) {} + for (j = ticks.length; ticks[j - 1] > v; j--) {} + ticks = ticks.slice(i, j); + } + return ticks; + }; + scale.tickFormat = function(n, format) { + if (arguments.length < 2) format = d3_scale_logFormat; + if (!arguments.length) return format; + var k = Math.max(.1, n / scale.ticks().length), f = log === d3_scale_logn ? (e = -1e-12, + Math.floor) : (e = 1e-12, Math.ceil), e; + return function(d) { + return d / pow(f(log(d) + e)) <= k ? format(d) : ""; + }; + }; + scale.copy = function() { + return d3_scale_log(linear.copy(), log); + }; + return d3_scale_linearRebind(scale, linear); + } + var d3_scale_logFormat = d3.format(".0e"); + function d3_scale_logp(x) { + return Math.log(x < 0 ? 0 : x) / Math.LN10; + } + function d3_scale_logn(x) { + return -Math.log(x > 0 ? 0 : -x) / Math.LN10; + } + d3_scale_logp.pow = function(x) { + return Math.pow(10, x); + }; + d3_scale_logn.pow = function(x) { + return -Math.pow(10, -x); + }; + d3.scale.pow = function() { + return d3_scale_pow(d3.scale.linear(), 1); + }; + function d3_scale_pow(linear, exponent) { + var powp = d3_scale_powPow(exponent), powb = d3_scale_powPow(1 / exponent); + function scale(x) { + return linear(powp(x)); + } + scale.invert = function(x) { + return powb(linear.invert(x)); + }; + scale.domain = function(x) { + if (!arguments.length) return linear.domain().map(powb); + linear.domain(x.map(powp)); + return scale; + }; + scale.ticks = function(m) { + return d3_scale_linearTicks(scale.domain(), m); + }; + scale.tickFormat = function(m) { + return d3_scale_linearTickFormat(scale.domain(), m); + }; + scale.nice = function() { + return scale.domain(d3_scale_nice(scale.domain(), d3_scale_linearNice)); + }; + scale.exponent = function(x) { + if (!arguments.length) return exponent; + var domain = scale.domain(); + powp = d3_scale_powPow(exponent = x); + powb = d3_scale_powPow(1 / exponent); + return scale.domain(domain); + }; + scale.copy = function() { + return d3_scale_pow(linear.copy(), exponent); + }; + return d3_scale_linearRebind(scale, linear); + } + function d3_scale_powPow(e) { + return function(x) { + return x < 0 ? -Math.pow(-x, e) : Math.pow(x, e); + }; + } + d3.scale.sqrt = function() { + return d3.scale.pow().exponent(.5); + }; + d3.scale.ordinal = function() { + return d3_scale_ordinal([], { + t: "range", + a: [ [] ] + }); + }; + function d3_scale_ordinal(domain, ranger) { + var index, range, rangeBand; + function scale(x) { + return range[((index.get(x) || index.set(x, domain.push(x))) - 1) % range.length]; + } + function steps(start, step) { + return d3.range(domain.length).map(function(i) { + return start + step * i; + }); + } + scale.domain = function(x) { + if (!arguments.length) return domain; + domain = []; + index = new d3_Map(); + var i = -1, n = x.length, xi; + while (++i < n) if (!index.has(xi = x[i])) index.set(xi, domain.push(xi)); + return scale[ranger.t].apply(scale, ranger.a); + }; + scale.range = function(x) { + if (!arguments.length) return range; + range = x; + rangeBand = 0; + ranger = { + t: "range", + a: arguments + }; + return scale; + }; + scale.rangePoints = function(x, padding) { + if (arguments.length < 2) padding = 0; + var start = x[0], stop = x[1], step = (stop - start) / (Math.max(1, domain.length - 1) + padding); + range = steps(domain.length < 2 ? (start + stop) / 2 : start + step * padding / 2, step); + rangeBand = 0; + ranger = { + t: "rangePoints", + a: arguments + }; + return scale; + }; + scale.rangeBands = function(x, padding, outerPadding) { + if (arguments.length < 2) padding = 0; + if (arguments.length < 3) outerPadding = padding; + var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = (stop - start) / (domain.length - padding + 2 * outerPadding); + range = steps(start + step * outerPadding, step); + if (reverse) range.reverse(); + rangeBand = step * (1 - padding); + ranger = { + t: "rangeBands", + a: arguments + }; + return scale; + }; + scale.rangeRoundBands = function(x, padding, outerPadding) { + if (arguments.length < 2) padding = 0; + if (arguments.length < 3) outerPadding = padding; + var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = Math.floor((stop - start) / (domain.length - padding + 2 * outerPadding)), error = stop - start - (domain.length - padding) * step; + range = steps(start + Math.round(error / 2), step); + if (reverse) range.reverse(); + rangeBand = Math.round(step * (1 - padding)); + ranger = { + t: "rangeRoundBands", + a: arguments + }; + return scale; + }; + scale.rangeBand = function() { + return rangeBand; + }; + scale.rangeExtent = function() { + return d3_scaleExtent(ranger.a[0]); + }; + scale.copy = function() { + return d3_scale_ordinal(domain, ranger); + }; + return scale.domain(domain); + } + d3.scale.category10 = function() { + return d3.scale.ordinal().range(d3_category10); + }; + d3.scale.category20 = function() { + return d3.scale.ordinal().range(d3_category20); + }; + d3.scale.category20b = function() { + return d3.scale.ordinal().range(d3_category20b); + }; + d3.scale.category20c = function() { + return d3.scale.ordinal().range(d3_category20c); + }; + var d3_category10 = [ "#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd", "#8c564b", "#e377c2", "#7f7f7f", "#bcbd22", "#17becf" ]; + var d3_category20 = [ "#1f77b4", "#aec7e8", "#ff7f0e", "#ffbb78", "#2ca02c", "#98df8a", "#d62728", "#ff9896", "#9467bd", "#c5b0d5", "#8c564b", "#c49c94", "#e377c2", "#f7b6d2", "#7f7f7f", "#c7c7c7", "#bcbd22", "#dbdb8d", "#17becf", "#9edae5" ]; + var d3_category20b = [ "#393b79", "#5254a3", "#6b6ecf", "#9c9ede", "#637939", "#8ca252", "#b5cf6b", "#cedb9c", "#8c6d31", "#bd9e39", "#e7ba52", "#e7cb94", "#843c39", "#ad494a", "#d6616b", "#e7969c", "#7b4173", "#a55194", "#ce6dbd", "#de9ed6" ]; + var d3_category20c = [ "#3182bd", "#6baed6", "#9ecae1", "#c6dbef", "#e6550d", "#fd8d3c", "#fdae6b", "#fdd0a2", "#31a354", "#74c476", "#a1d99b", "#c7e9c0", "#756bb1", "#9e9ac8", "#bcbddc", "#dadaeb", "#636363", "#969696", "#bdbdbd", "#d9d9d9" ]; + d3.scale.quantile = function() { + return d3_scale_quantile([], []); + }; + function d3_scale_quantile(domain, range) { + var thresholds; + function rescale() { + var k = 0, q = range.length; + thresholds = []; + while (++k < q) thresholds[k - 1] = d3.quantile(domain, k / q); + return scale; + } + function scale(x) { + if (isNaN(x = +x)) return NaN; + return range[d3.bisect(thresholds, x)]; + } + scale.domain = function(x) { + if (!arguments.length) return domain; + domain = x.filter(function(d) { + return !isNaN(d); + }).sort(d3.ascending); + return rescale(); + }; + scale.range = function(x) { + if (!arguments.length) return range; + range = x; + return rescale(); + }; + scale.quantiles = function() { + return thresholds; + }; + scale.copy = function() { + return d3_scale_quantile(domain, range); + }; + return rescale(); + } + d3.scale.quantize = function() { + return d3_scale_quantize(0, 1, [ 0, 1 ]); + }; + function d3_scale_quantize(x0, x1, range) { + var kx, i; + function scale(x) { + return range[Math.max(0, Math.min(i, Math.floor(kx * (x - x0))))]; + } + function rescale() { + kx = range.length / (x1 - x0); + i = range.length - 1; + return scale; + } + scale.domain = function(x) { + if (!arguments.length) return [ x0, x1 ]; + x0 = +x[0]; + x1 = +x[x.length - 1]; + return rescale(); + }; + scale.range = function(x) { + if (!arguments.length) return range; + range = x; + return rescale(); + }; + scale.copy = function() { + return d3_scale_quantize(x0, x1, range); + }; + return rescale(); + } + d3.scale.threshold = function() { + return d3_scale_threshold([ .5 ], [ 0, 1 ]); + }; + function d3_scale_threshold(domain, range) { + function scale(x) { + return range[d3.bisect(domain, x)]; + } + scale.domain = function(_) { + if (!arguments.length) return domain; + domain = _; + return scale; + }; + scale.range = function(_) { + if (!arguments.length) return range; + range = _; + return scale; + }; + scale.copy = function() { + return d3_scale_threshold(domain, range); + }; + return scale; + } + d3.scale.identity = function() { + return d3_scale_identity([ 0, 1 ]); + }; + function d3_scale_identity(domain) { + function identity(x) { + return +x; + } + identity.invert = identity; + identity.domain = identity.range = function(x) { + if (!arguments.length) return domain; + domain = x.map(identity); + return identity; + }; + identity.ticks = function(m) { + return d3_scale_linearTicks(domain, m); + }; + identity.tickFormat = function(m) { + return d3_scale_linearTickFormat(domain, m); + }; + identity.copy = function() { + return d3_scale_identity(domain); + }; + return identity; + } + d3.svg = {}; + d3.svg.arc = function() { + var innerRadius = d3_svg_arcInnerRadius, outerRadius = d3_svg_arcOuterRadius, startAngle = d3_svg_arcStartAngle, endAngle = d3_svg_arcEndAngle; + function arc() { + var r0 = innerRadius.apply(this, arguments), r1 = outerRadius.apply(this, arguments), a0 = startAngle.apply(this, arguments) + d3_svg_arcOffset, a1 = endAngle.apply(this, arguments) + d3_svg_arcOffset, da = (a1 < a0 && (da = a0, + a0 = a1, a1 = da), a1 - a0), df = da < π ? "0" : "1", c0 = Math.cos(a0), s0 = Math.sin(a0), c1 = Math.cos(a1), s1 = Math.sin(a1); + return da >= d3_svg_arcMax ? r0 ? "M0," + r1 + "A" + r1 + "," + r1 + " 0 1,1 0," + -r1 + "A" + r1 + "," + r1 + " 0 1,1 0," + r1 + "M0," + r0 + "A" + r0 + "," + r0 + " 0 1,0 0," + -r0 + "A" + r0 + "," + r0 + " 0 1,0 0," + r0 + "Z" : "M0," + r1 + "A" + r1 + "," + r1 + " 0 1,1 0," + -r1 + "A" + r1 + "," + r1 + " 0 1,1 0," + r1 + "Z" : r0 ? "M" + r1 * c0 + "," + r1 * s0 + "A" + r1 + "," + r1 + " 0 " + df + ",1 " + r1 * c1 + "," + r1 * s1 + "L" + r0 * c1 + "," + r0 * s1 + "A" + r0 + "," + r0 + " 0 " + df + ",0 " + r0 * c0 + "," + r0 * s0 + "Z" : "M" + r1 * c0 + "," + r1 * s0 + "A" + r1 + "," + r1 + " 0 " + df + ",1 " + r1 * c1 + "," + r1 * s1 + "L0,0" + "Z"; + } + arc.innerRadius = function(v) { + if (!arguments.length) return innerRadius; + innerRadius = d3_functor(v); + return arc; + }; + arc.outerRadius = function(v) { + if (!arguments.length) return outerRadius; + outerRadius = d3_functor(v); + return arc; + }; + arc.startAngle = function(v) { + if (!arguments.length) return startAngle; + startAngle = d3_functor(v); + return arc; + }; + arc.endAngle = function(v) { + if (!arguments.length) return endAngle; + endAngle = d3_functor(v); + return arc; + }; + arc.centroid = function() { + var r = (innerRadius.apply(this, arguments) + outerRadius.apply(this, arguments)) / 2, a = (startAngle.apply(this, arguments) + endAngle.apply(this, arguments)) / 2 + d3_svg_arcOffset; + return [ Math.cos(a) * r, Math.sin(a) * r ]; + }; + return arc; + }; + var d3_svg_arcOffset = -π / 2, d3_svg_arcMax = 2 * π - 1e-6; + function d3_svg_arcInnerRadius(d) { + return d.innerRadius; + } + function d3_svg_arcOuterRadius(d) { + return d.outerRadius; + } + function d3_svg_arcStartAngle(d) { + return d.startAngle; + } + function d3_svg_arcEndAngle(d) { + return d.endAngle; + } + function d3_svg_line(projection) { + var x = d3_svg_lineX, y = d3_svg_lineY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, tension = .7; + function line(data) { + var segments = [], points = [], i = -1, n = data.length, d, fx = d3_functor(x), fy = d3_functor(y); + function segment() { + segments.push("M", interpolate(projection(points), tension)); + } + while (++i < n) { + if (defined.call(this, d = data[i], i)) { + points.push([ +fx.call(this, d, i), +fy.call(this, d, i) ]); + } else if (points.length) { + segment(); + points = []; + } + } + if (points.length) segment(); + return segments.length ? segments.join("") : null; + } + line.x = function(_) { + if (!arguments.length) return x; + x = _; + return line; + }; + line.y = function(_) { + if (!arguments.length) return y; + y = _; + return line; + }; + line.defined = function(_) { + if (!arguments.length) return defined; + defined = _; + return line; + }; + line.interpolate = function(_) { + if (!arguments.length) return interpolateKey; + if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key; + return line; + }; + line.tension = function(_) { + if (!arguments.length) return tension; + tension = _; + return line; + }; + return line; + } + d3.svg.line = function() { + return d3_svg_line(d3_identity); + }; + function d3_svg_lineX(d) { + return d[0]; + } + function d3_svg_lineY(d) { + return d[1]; + } + var d3_svg_lineInterpolators = d3.map({ + linear: d3_svg_lineLinear, + "linear-closed": d3_svg_lineLinearClosed, + "step-before": d3_svg_lineStepBefore, + "step-after": d3_svg_lineStepAfter, + basis: d3_svg_lineBasis, + "basis-open": d3_svg_lineBasisOpen, + "basis-closed": d3_svg_lineBasisClosed, + bundle: d3_svg_lineBundle, + cardinal: d3_svg_lineCardinal, + "cardinal-open": d3_svg_lineCardinalOpen, + "cardinal-closed": d3_svg_lineCardinalClosed, + monotone: d3_svg_lineMonotone + }); + d3_svg_lineInterpolators.forEach(function(key, value) { + value.key = key; + value.closed = /-closed$/.test(key); + }); + function d3_svg_lineLinear(points) { + return points.join("L"); + } + function d3_svg_lineLinearClosed(points) { + return d3_svg_lineLinear(points) + "Z"; + } + function d3_svg_lineStepBefore(points) { + var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ]; + while (++i < n) path.push("V", (p = points[i])[1], "H", p[0]); + return path.join(""); + } + function d3_svg_lineStepAfter(points) { + var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ]; + while (++i < n) path.push("H", (p = points[i])[0], "V", p[1]); + return path.join(""); + } + function d3_svg_lineCardinalOpen(points, tension) { + return points.length < 4 ? d3_svg_lineLinear(points) : points[1] + d3_svg_lineHermite(points.slice(1, points.length - 1), d3_svg_lineCardinalTangents(points, tension)); + } + function d3_svg_lineCardinalClosed(points, tension) { + return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite((points.push(points[0]), + points), d3_svg_lineCardinalTangents([ points[points.length - 2] ].concat(points, [ points[1] ]), tension)); + } + function d3_svg_lineCardinal(points, tension) { + return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineCardinalTangents(points, tension)); + } + function d3_svg_lineHermite(points, tangents) { + if (tangents.length < 1 || points.length != tangents.length && points.length != tangents.length + 2) { + return d3_svg_lineLinear(points); + } + var quad = points.length != tangents.length, path = "", p0 = points[0], p = points[1], t0 = tangents[0], t = t0, pi = 1; + if (quad) { + path += "Q" + (p[0] - t0[0] * 2 / 3) + "," + (p[1] - t0[1] * 2 / 3) + "," + p[0] + "," + p[1]; + p0 = points[1]; + pi = 2; + } + if (tangents.length > 1) { + t = tangents[1]; + p = points[pi]; + pi++; + path += "C" + (p0[0] + t0[0]) + "," + (p0[1] + t0[1]) + "," + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1]; + for (var i = 2; i < tangents.length; i++, pi++) { + p = points[pi]; + t = tangents[i]; + path += "S" + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1]; + } + } + if (quad) { + var lp = points[pi]; + path += "Q" + (p[0] + t[0] * 2 / 3) + "," + (p[1] + t[1] * 2 / 3) + "," + lp[0] + "," + lp[1]; + } + return path; + } + function d3_svg_lineCardinalTangents(points, tension) { + var tangents = [], a = (1 - tension) / 2, p0, p1 = points[0], p2 = points[1], i = 1, n = points.length; + while (++i < n) { + p0 = p1; + p1 = p2; + p2 = points[i]; + tangents.push([ a * (p2[0] - p0[0]), a * (p2[1] - p0[1]) ]); + } + return tangents; + } + function d3_svg_lineBasis(points) { + if (points.length < 3) return d3_svg_lineLinear(points); + var i = 1, n = points.length, pi = points[0], x0 = pi[0], y0 = pi[1], px = [ x0, x0, x0, (pi = points[1])[0] ], py = [ y0, y0, y0, pi[1] ], path = [ x0, ",", y0 ]; + d3_svg_lineBasisBezier(path, px, py); + while (++i < n) { + pi = points[i]; + px.shift(); + px.push(pi[0]); + py.shift(); + py.push(pi[1]); + d3_svg_lineBasisBezier(path, px, py); + } + i = -1; + while (++i < 2) { + px.shift(); + px.push(pi[0]); + py.shift(); + py.push(pi[1]); + d3_svg_lineBasisBezier(path, px, py); + } + return path.join(""); + } + function d3_svg_lineBasisOpen(points) { + if (points.length < 4) return d3_svg_lineLinear(points); + var path = [], i = -1, n = points.length, pi, px = [ 0 ], py = [ 0 ]; + while (++i < 3) { + pi = points[i]; + px.push(pi[0]); + py.push(pi[1]); + } + path.push(d3_svg_lineDot4(d3_svg_lineBasisBezier3, px) + "," + d3_svg_lineDot4(d3_svg_lineBasisBezier3, py)); + --i; + while (++i < n) { + pi = points[i]; + px.shift(); + px.push(pi[0]); + py.shift(); + py.push(pi[1]); + d3_svg_lineBasisBezier(path, px, py); + } + return path.join(""); + } + function d3_svg_lineBasisClosed(points) { + var path, i = -1, n = points.length, m = n + 4, pi, px = [], py = []; + while (++i < 4) { + pi = points[i % n]; + px.push(pi[0]); + py.push(pi[1]); + } + path = [ d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, py) ]; + --i; + while (++i < m) { + pi = points[i % n]; + px.shift(); + px.push(pi[0]); + py.shift(); + py.push(pi[1]); + d3_svg_lineBasisBezier(path, px, py); + } + return path.join(""); + } + function d3_svg_lineBundle(points, tension) { + var n = points.length - 1; + if (n) { + var x0 = points[0][0], y0 = points[0][1], dx = points[n][0] - x0, dy = points[n][1] - y0, i = -1, p, t; + while (++i <= n) { + p = points[i]; + t = i / n; + p[0] = tension * p[0] + (1 - tension) * (x0 + t * dx); + p[1] = tension * p[1] + (1 - tension) * (y0 + t * dy); + } + } + return d3_svg_lineBasis(points); + } + function d3_svg_lineDot4(a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]; + } + var d3_svg_lineBasisBezier1 = [ 0, 2 / 3, 1 / 3, 0 ], d3_svg_lineBasisBezier2 = [ 0, 1 / 3, 2 / 3, 0 ], d3_svg_lineBasisBezier3 = [ 0, 1 / 6, 2 / 3, 1 / 6 ]; + function d3_svg_lineBasisBezier(path, x, y) { + path.push("C", d3_svg_lineDot4(d3_svg_lineBasisBezier1, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier1, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, y)); + } + function d3_svg_lineSlope(p0, p1) { + return (p1[1] - p0[1]) / (p1[0] - p0[0]); + } + function d3_svg_lineFiniteDifferences(points) { + var i = 0, j = points.length - 1, m = [], p0 = points[0], p1 = points[1], d = m[0] = d3_svg_lineSlope(p0, p1); + while (++i < j) { + m[i] = (d + (d = d3_svg_lineSlope(p0 = p1, p1 = points[i + 1]))) / 2; + } + m[i] = d; + return m; + } + function d3_svg_lineMonotoneTangents(points) { + var tangents = [], d, a, b, s, m = d3_svg_lineFiniteDifferences(points), i = -1, j = points.length - 1; + while (++i < j) { + d = d3_svg_lineSlope(points[i], points[i + 1]); + if (Math.abs(d) < 1e-6) { + m[i] = m[i + 1] = 0; + } else { + a = m[i] / d; + b = m[i + 1] / d; + s = a * a + b * b; + if (s > 9) { + s = d * 3 / Math.sqrt(s); + m[i] = s * a; + m[i + 1] = s * b; + } + } + } + i = -1; + while (++i <= j) { + s = (points[Math.min(j, i + 1)][0] - points[Math.max(0, i - 1)][0]) / (6 * (1 + m[i] * m[i])); + tangents.push([ s || 0, m[i] * s || 0 ]); + } + return tangents; + } + function d3_svg_lineMonotone(points) { + return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineMonotoneTangents(points)); + } + d3.svg.line.radial = function() { + var line = d3_svg_line(d3_svg_lineRadial); + line.radius = line.x, delete line.x; + line.angle = line.y, delete line.y; + return line; + }; + function d3_svg_lineRadial(points) { + var point, i = -1, n = points.length, r, a; + while (++i < n) { + point = points[i]; + r = point[0]; + a = point[1] + d3_svg_arcOffset; + point[0] = r * Math.cos(a); + point[1] = r * Math.sin(a); + } + return points; + } + function d3_svg_area(projection) { + var x0 = d3_svg_lineX, x1 = d3_svg_lineX, y0 = 0, y1 = d3_svg_lineY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, interpolateReverse = interpolate, L = "L", tension = .7; + function area(data) { + var segments = [], points0 = [], points1 = [], i = -1, n = data.length, d, fx0 = d3_functor(x0), fy0 = d3_functor(y0), fx1 = x0 === x1 ? function() { + return x; + } : d3_functor(x1), fy1 = y0 === y1 ? function() { + return y; + } : d3_functor(y1), x, y; + function segment() { + segments.push("M", interpolate(projection(points1), tension), L, interpolateReverse(projection(points0.reverse()), tension), "Z"); + } + while (++i < n) { + if (defined.call(this, d = data[i], i)) { + points0.push([ x = +fx0.call(this, d, i), y = +fy0.call(this, d, i) ]); + points1.push([ +fx1.call(this, d, i), +fy1.call(this, d, i) ]); + } else if (points0.length) { + segment(); + points0 = []; + points1 = []; + } + } + if (points0.length) segment(); + return segments.length ? segments.join("") : null; + } + area.x = function(_) { + if (!arguments.length) return x1; + x0 = x1 = _; + return area; + }; + area.x0 = function(_) { + if (!arguments.length) return x0; + x0 = _; + return area; + }; + area.x1 = function(_) { + if (!arguments.length) return x1; + x1 = _; + return area; + }; + area.y = function(_) { + if (!arguments.length) return y1; + y0 = y1 = _; + return area; + }; + area.y0 = function(_) { + if (!arguments.length) return y0; + y0 = _; + return area; + }; + area.y1 = function(_) { + if (!arguments.length) return y1; + y1 = _; + return area; + }; + area.defined = function(_) { + if (!arguments.length) return defined; + defined = _; + return area; + }; + area.interpolate = function(_) { + if (!arguments.length) return interpolateKey; + if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key; + interpolateReverse = interpolate.reverse || interpolate; + L = interpolate.closed ? "M" : "L"; + return area; + }; + area.tension = function(_) { + if (!arguments.length) return tension; + tension = _; + return area; + }; + return area; + } + d3_svg_lineStepBefore.reverse = d3_svg_lineStepAfter; + d3_svg_lineStepAfter.reverse = d3_svg_lineStepBefore; + d3.svg.area = function() { + return d3_svg_area(d3_identity); + }; + d3.svg.area.radial = function() { + var area = d3_svg_area(d3_svg_lineRadial); + area.radius = area.x, delete area.x; + area.innerRadius = area.x0, delete area.x0; + area.outerRadius = area.x1, delete area.x1; + area.angle = area.y, delete area.y; + area.startAngle = area.y0, delete area.y0; + area.endAngle = area.y1, delete area.y1; + return area; + }; + d3.svg.chord = function() { + var source = d3_source, target = d3_target, radius = d3_svg_chordRadius, startAngle = d3_svg_arcStartAngle, endAngle = d3_svg_arcEndAngle; + function chord(d, i) { + var s = subgroup(this, source, d, i), t = subgroup(this, target, d, i); + return "M" + s.p0 + arc(s.r, s.p1, s.a1 - s.a0) + (equals(s, t) ? curve(s.r, s.p1, s.r, s.p0) : curve(s.r, s.p1, t.r, t.p0) + arc(t.r, t.p1, t.a1 - t.a0) + curve(t.r, t.p1, s.r, s.p0)) + "Z"; + } + function subgroup(self, f, d, i) { + var subgroup = f.call(self, d, i), r = radius.call(self, subgroup, i), a0 = startAngle.call(self, subgroup, i) + d3_svg_arcOffset, a1 = endAngle.call(self, subgroup, i) + d3_svg_arcOffset; + return { + r: r, + a0: a0, + a1: a1, + p0: [ r * Math.cos(a0), r * Math.sin(a0) ], + p1: [ r * Math.cos(a1), r * Math.sin(a1) ] + }; + } + function equals(a, b) { + return a.a0 == b.a0 && a.a1 == b.a1; + } + function arc(r, p, a) { + return "A" + r + "," + r + " 0 " + +(a > π) + ",1 " + p; + } + function curve(r0, p0, r1, p1) { + return "Q 0,0 " + p1; + } + chord.radius = function(v) { + if (!arguments.length) return radius; + radius = d3_functor(v); + return chord; + }; + chord.source = function(v) { + if (!arguments.length) return source; + source = d3_functor(v); + return chord; + }; + chord.target = function(v) { + if (!arguments.length) return target; + target = d3_functor(v); + return chord; + }; + chord.startAngle = function(v) { + if (!arguments.length) return startAngle; + startAngle = d3_functor(v); + return chord; + }; + chord.endAngle = function(v) { + if (!arguments.length) return endAngle; + endAngle = d3_functor(v); + return chord; + }; + return chord; + }; + function d3_svg_chordRadius(d) { + return d.radius; + } + d3.svg.diagonal = function() { + var source = d3_source, target = d3_target, projection = d3_svg_diagonalProjection; + function diagonal(d, i) { + var p0 = source.call(this, d, i), p3 = target.call(this, d, i), m = (p0.y + p3.y) / 2, p = [ p0, { + x: p0.x, + y: m + }, { + x: p3.x, + y: m + }, p3 ]; + p = p.map(projection); + return "M" + p[0] + "C" + p[1] + " " + p[2] + " " + p[3]; + } + diagonal.source = function(x) { + if (!arguments.length) return source; + source = d3_functor(x); + return diagonal; + }; + diagonal.target = function(x) { + if (!arguments.length) return target; + target = d3_functor(x); + return diagonal; + }; + diagonal.projection = function(x) { + if (!arguments.length) return projection; + projection = x; + return diagonal; + }; + return diagonal; + }; + function d3_svg_diagonalProjection(d) { + return [ d.x, d.y ]; + } + d3.svg.diagonal.radial = function() { + var diagonal = d3.svg.diagonal(), projection = d3_svg_diagonalProjection, projection_ = diagonal.projection; + diagonal.projection = function(x) { + return arguments.length ? projection_(d3_svg_diagonalRadialProjection(projection = x)) : projection; + }; + return diagonal; + }; + function d3_svg_diagonalRadialProjection(projection) { + return function() { + var d = projection.apply(this, arguments), r = d[0], a = d[1] + d3_svg_arcOffset; + return [ r * Math.cos(a), r * Math.sin(a) ]; + }; + } + d3.svg.symbol = function() { + var type = d3_svg_symbolType, size = d3_svg_symbolSize; + function symbol(d, i) { + return (d3_svg_symbols.get(type.call(this, d, i)) || d3_svg_symbolCircle)(size.call(this, d, i)); + } + symbol.type = function(x) { + if (!arguments.length) return type; + type = d3_functor(x); + return symbol; + }; + symbol.size = function(x) { + if (!arguments.length) return size; + size = d3_functor(x); + return symbol; + }; + return symbol; + }; + function d3_svg_symbolSize() { + return 64; + } + function d3_svg_symbolType() { + return "circle"; + } + function d3_svg_symbolCircle(size) { + var r = Math.sqrt(size / π); + return "M0," + r + "A" + r + "," + r + " 0 1,1 0," + -r + "A" + r + "," + r + " 0 1,1 0," + r + "Z"; + } + var d3_svg_symbols = d3.map({ + circle: d3_svg_symbolCircle, + cross: function(size) { + var r = Math.sqrt(size / 5) / 2; + return "M" + -3 * r + "," + -r + "H" + -r + "V" + -3 * r + "H" + r + "V" + -r + "H" + 3 * r + "V" + r + "H" + r + "V" + 3 * r + "H" + -r + "V" + r + "H" + -3 * r + "Z"; + }, + diamond: function(size) { + var ry = Math.sqrt(size / (2 * d3_svg_symbolTan30)), rx = ry * d3_svg_symbolTan30; + return "M0," + -ry + "L" + rx + ",0" + " 0," + ry + " " + -rx + ",0" + "Z"; + }, + square: function(size) { + var r = Math.sqrt(size) / 2; + return "M" + -r + "," + -r + "L" + r + "," + -r + " " + r + "," + r + " " + -r + "," + r + "Z"; + }, + "triangle-down": function(size) { + var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2; + return "M0," + ry + "L" + rx + "," + -ry + " " + -rx + "," + -ry + "Z"; + }, + "triangle-up": function(size) { + var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2; + return "M0," + -ry + "L" + rx + "," + ry + " " + -rx + "," + ry + "Z"; + } + }); + d3.svg.symbolTypes = d3_svg_symbols.keys(); + var d3_svg_symbolSqrt3 = Math.sqrt(3), d3_svg_symbolTan30 = Math.tan(30 * d3_radians); + d3.svg.axis = function() { + var scale = d3.scale.linear(), orient = d3_svg_axisDefaultOrient, tickMajorSize = 6, tickMinorSize = 6, tickEndSize = 6, tickPadding = 3, tickArguments_ = [ 10 ], tickValues = null, tickFormat_, tickSubdivide = 0; + function axis(g) { + g.each(function() { + var g = d3.select(this); + var ticks = tickValues == null ? scale.ticks ? scale.ticks.apply(scale, tickArguments_) : scale.domain() : tickValues, tickFormat = tickFormat_ == null ? scale.tickFormat ? scale.tickFormat.apply(scale, tickArguments_) : String : tickFormat_; + var subticks = d3_svg_axisSubdivide(scale, ticks, tickSubdivide), subtick = g.selectAll(".tick.minor").data(subticks, String), subtickEnter = subtick.enter().insert("line", ".tick").attr("class", "tick minor").style("opacity", 1e-6), subtickExit = d3.transition(subtick.exit()).style("opacity", 1e-6).remove(), subtickUpdate = d3.transition(subtick).style("opacity", 1); + var tick = g.selectAll(".tick.major").data(ticks, String), tickEnter = tick.enter().insert("g", "path").attr("class", "tick major").style("opacity", 1e-6), tickExit = d3.transition(tick.exit()).style("opacity", 1e-6).remove(), tickUpdate = d3.transition(tick).style("opacity", 1), tickTransform; + var range = d3_scaleRange(scale), path = g.selectAll(".domain").data([ 0 ]), pathUpdate = (path.enter().append("path").attr("class", "domain"), + d3.transition(path)); + var scale1 = scale.copy(), scale0 = this.__chart__ || scale1; + this.__chart__ = scale1; + tickEnter.append("line"); + tickEnter.append("text"); + var lineEnter = tickEnter.select("line"), lineUpdate = tickUpdate.select("line"), text = tick.select("text").text(tickFormat), textEnter = tickEnter.select("text"), textUpdate = tickUpdate.select("text"); + switch (orient) { + case "bottom": + { + tickTransform = d3_svg_axisX; + subtickEnter.attr("y2", tickMinorSize); + subtickUpdate.attr("x2", 0).attr("y2", tickMinorSize); + lineEnter.attr("y2", tickMajorSize); + textEnter.attr("y", Math.max(tickMajorSize, 0) + tickPadding); + lineUpdate.attr("x2", 0).attr("y2", tickMajorSize); + textUpdate.attr("x", 0).attr("y", Math.max(tickMajorSize, 0) + tickPadding); + text.attr("dy", ".71em").style("text-anchor", "middle"); + pathUpdate.attr("d", "M" + range[0] + "," + tickEndSize + "V0H" + range[1] + "V" + tickEndSize); + break; + } + + case "top": + { + tickTransform = d3_svg_axisX; + subtickEnter.attr("y2", -tickMinorSize); + subtickUpdate.attr("x2", 0).attr("y2", -tickMinorSize); + lineEnter.attr("y2", -tickMajorSize); + textEnter.attr("y", -(Math.max(tickMajorSize, 0) + tickPadding)); + lineUpdate.attr("x2", 0).attr("y2", -tickMajorSize); + textUpdate.attr("x", 0).attr("y", -(Math.max(tickMajorSize, 0) + tickPadding)); + text.attr("dy", "0em").style("text-anchor", "middle"); + pathUpdate.attr("d", "M" + range[0] + "," + -tickEndSize + "V0H" + range[1] + "V" + -tickEndSize); + break; + } + + case "left": + { + tickTransform = d3_svg_axisY; + subtickEnter.attr("x2", -tickMinorSize); + subtickUpdate.attr("x2", -tickMinorSize).attr("y2", 0); + lineEnter.attr("x2", -tickMajorSize); + textEnter.attr("x", -(Math.max(tickMajorSize, 0) + tickPadding)); + lineUpdate.attr("x2", -tickMajorSize).attr("y2", 0); + textUpdate.attr("x", -(Math.max(tickMajorSize, 0) + tickPadding)).attr("y", 0); + text.attr("dy", ".32em").style("text-anchor", "end"); + pathUpdate.attr("d", "M" + -tickEndSize + "," + range[0] + "H0V" + range[1] + "H" + -tickEndSize); + break; + } + + case "right": + { + tickTransform = d3_svg_axisY; + subtickEnter.attr("x2", tickMinorSize); + subtickUpdate.attr("x2", tickMinorSize).attr("y2", 0); + lineEnter.attr("x2", tickMajorSize); + textEnter.attr("x", Math.max(tickMajorSize, 0) + tickPadding); + lineUpdate.attr("x2", tickMajorSize).attr("y2", 0); + textUpdate.attr("x", Math.max(tickMajorSize, 0) + tickPadding).attr("y", 0); + text.attr("dy", ".32em").style("text-anchor", "start"); + pathUpdate.attr("d", "M" + tickEndSize + "," + range[0] + "H0V" + range[1] + "H" + tickEndSize); + break; + } + } + if (scale.ticks) { + tickEnter.call(tickTransform, scale0); + tickUpdate.call(tickTransform, scale1); + tickExit.call(tickTransform, scale1); + subtickEnter.call(tickTransform, scale0); + subtickUpdate.call(tickTransform, scale1); + subtickExit.call(tickTransform, scale1); + } else { + var dx = scale1.rangeBand() / 2, x = function(d) { + return scale1(d) + dx; + }; + tickEnter.call(tickTransform, x); + tickUpdate.call(tickTransform, x); + } + }); + } + axis.scale = function(x) { + if (!arguments.length) return scale; + scale = x; + return axis; + }; + axis.orient = function(x) { + if (!arguments.length) return orient; + orient = x in d3_svg_axisOrients ? x + "" : d3_svg_axisDefaultOrient; + return axis; + }; + axis.ticks = function() { + if (!arguments.length) return tickArguments_; + tickArguments_ = arguments; + return axis; + }; + axis.tickValues = function(x) { + if (!arguments.length) return tickValues; + tickValues = x; + return axis; + }; + axis.tickFormat = function(x) { + if (!arguments.length) return tickFormat_; + tickFormat_ = x; + return axis; + }; + axis.tickSize = function(x, y) { + if (!arguments.length) return tickMajorSize; + var n = arguments.length - 1; + tickMajorSize = +x; + tickMinorSize = n > 1 ? +y : tickMajorSize; + tickEndSize = n > 0 ? +arguments[n] : tickMajorSize; + return axis; + }; + axis.tickPadding = function(x) { + if (!arguments.length) return tickPadding; + tickPadding = +x; + return axis; + }; + axis.tickSubdivide = function(x) { + if (!arguments.length) return tickSubdivide; + tickSubdivide = +x; + return axis; + }; + return axis; + }; + var d3_svg_axisDefaultOrient = "bottom", d3_svg_axisOrients = { + top: 1, + right: 1, + bottom: 1, + left: 1 + }; + function d3_svg_axisX(selection, x) { + selection.attr("transform", function(d) { + return "translate(" + x(d) + ",0)"; + }); + } + function d3_svg_axisY(selection, y) { + selection.attr("transform", function(d) { + return "translate(0," + y(d) + ")"; + }); + } + function d3_svg_axisSubdivide(scale, ticks, m) { + subticks = []; + if (m && ticks.length > 1) { + var extent = d3_scaleExtent(scale.domain()), subticks, i = -1, n = ticks.length, d = (ticks[1] - ticks[0]) / ++m, j, v; + while (++i < n) { + for (j = m; --j > 0; ) { + if ((v = +ticks[i] - j * d) >= extent[0]) { + subticks.push(v); + } + } + } + for (--i, j = 0; ++j < m && (v = +ticks[i] + j * d) < extent[1]; ) { + subticks.push(v); + } + } + return subticks; + } + d3.svg.brush = function() { + var event = d3_eventDispatch(brush, "brushstart", "brush", "brushend"), x = null, y = null, resizes = d3_svg_brushResizes[0], extent = [ [ 0, 0 ], [ 0, 0 ] ], extentDomain; + function brush(g) { + g.each(function() { + var g = d3.select(this), bg = g.selectAll(".background").data([ 0 ]), fg = g.selectAll(".extent").data([ 0 ]), tz = g.selectAll(".resize").data(resizes, String), e; + g.style("pointer-events", "all").on("mousedown.brush", brushstart).on("touchstart.brush", brushstart); + bg.enter().append("rect").attr("class", "background").style("visibility", "hidden").style("cursor", "crosshair"); + fg.enter().append("rect").attr("class", "extent").style("cursor", "move"); + tz.enter().append("g").attr("class", function(d) { + return "resize " + d; + }).style("cursor", function(d) { + return d3_svg_brushCursor[d]; + }).append("rect").attr("x", function(d) { + return /[ew]$/.test(d) ? -3 : null; + }).attr("y", function(d) { + return /^[ns]/.test(d) ? -3 : null; + }).attr("width", 6).attr("height", 6).style("visibility", "hidden"); + tz.style("display", brush.empty() ? "none" : null); + tz.exit().remove(); + if (x) { + e = d3_scaleRange(x); + bg.attr("x", e[0]).attr("width", e[1] - e[0]); + redrawX(g); + } + if (y) { + e = d3_scaleRange(y); + bg.attr("y", e[0]).attr("height", e[1] - e[0]); + redrawY(g); + } + redraw(g); + }); + } + function redraw(g) { + g.selectAll(".resize").attr("transform", function(d) { + return "translate(" + extent[+/e$/.test(d)][0] + "," + extent[+/^s/.test(d)][1] + ")"; + }); + } + function redrawX(g) { + g.select(".extent").attr("x", extent[0][0]); + g.selectAll(".extent,.n>rect,.s>rect").attr("width", extent[1][0] - extent[0][0]); + } + function redrawY(g) { + g.select(".extent").attr("y", extent[0][1]); + g.selectAll(".extent,.e>rect,.w>rect").attr("height", extent[1][1] - extent[0][1]); + } + function brushstart() { + var target = this, eventTarget = d3.select(d3.event.target), event_ = event.of(target, arguments), g = d3.select(target), resizing = eventTarget.datum(), resizingX = !/^(n|s)$/.test(resizing) && x, resizingY = !/^(e|w)$/.test(resizing) && y, dragging = eventTarget.classed("extent"), center, origin = mouse(), offset; + var w = d3.select(d3_window).on("mousemove.brush", brushmove).on("mouseup.brush", brushend).on("touchmove.brush", brushmove).on("touchend.brush", brushend).on("keydown.brush", keydown).on("keyup.brush", keyup); + if (dragging) { + origin[0] = extent[0][0] - origin[0]; + origin[1] = extent[0][1] - origin[1]; + } else if (resizing) { + var ex = +/w$/.test(resizing), ey = +/^n/.test(resizing); + offset = [ extent[1 - ex][0] - origin[0], extent[1 - ey][1] - origin[1] ]; + origin[0] = extent[ex][0]; + origin[1] = extent[ey][1]; + } else if (d3.event.altKey) center = origin.slice(); + g.style("pointer-events", "none").selectAll(".resize").style("display", null); + d3.select("body").style("cursor", eventTarget.style("cursor")); + event_({ + type: "brushstart" + }); + brushmove(); + d3_eventCancel(); + function mouse() { + var touches = d3.event.changedTouches; + return touches ? d3.touches(target, touches)[0] : d3.mouse(target); + } + function keydown() { + if (d3.event.keyCode == 32) { + if (!dragging) { + center = null; + origin[0] -= extent[1][0]; + origin[1] -= extent[1][1]; + dragging = 2; + } + d3_eventCancel(); + } + } + function keyup() { + if (d3.event.keyCode == 32 && dragging == 2) { + origin[0] += extent[1][0]; + origin[1] += extent[1][1]; + dragging = 0; + d3_eventCancel(); + } + } + function brushmove() { + var point = mouse(), moved = false; + if (offset) { + point[0] += offset[0]; + point[1] += offset[1]; + } + if (!dragging) { + if (d3.event.altKey) { + if (!center) center = [ (extent[0][0] + extent[1][0]) / 2, (extent[0][1] + extent[1][1]) / 2 ]; + origin[0] = extent[+(point[0] < center[0])][0]; + origin[1] = extent[+(point[1] < center[1])][1]; + } else center = null; + } + if (resizingX && move1(point, x, 0)) { + redrawX(g); + moved = true; + } + if (resizingY && move1(point, y, 1)) { + redrawY(g); + moved = true; + } + if (moved) { + redraw(g); + event_({ + type: "brush", + mode: dragging ? "move" : "resize" + }); + } + } + function move1(point, scale, i) { + var range = d3_scaleRange(scale), r0 = range[0], r1 = range[1], position = origin[i], size = extent[1][i] - extent[0][i], min, max; + if (dragging) { + r0 -= position; + r1 -= size + position; + } + min = Math.max(r0, Math.min(r1, point[i])); + if (dragging) { + max = (min += position) + size; + } else { + if (center) position = Math.max(r0, Math.min(r1, 2 * center[i] - min)); + if (position < min) { + max = min; + min = position; + } else { + max = position; + } + } + if (extent[0][i] !== min || extent[1][i] !== max) { + extentDomain = null; + extent[0][i] = min; + extent[1][i] = max; + return true; + } + } + function brushend() { + brushmove(); + g.style("pointer-events", "all").selectAll(".resize").style("display", brush.empty() ? "none" : null); + d3.select("body").style("cursor", null); + w.on("mousemove.brush", null).on("mouseup.brush", null).on("touchmove.brush", null).on("touchend.brush", null).on("keydown.brush", null).on("keyup.brush", null); + event_({ + type: "brushend" + }); + d3_eventCancel(); + } + } + brush.x = function(z) { + if (!arguments.length) return x; + x = z; + resizes = d3_svg_brushResizes[!x << 1 | !y]; + return brush; + }; + brush.y = function(z) { + if (!arguments.length) return y; + y = z; + resizes = d3_svg_brushResizes[!x << 1 | !y]; + return brush; + }; + brush.extent = function(z) { + var x0, x1, y0, y1, t; + if (!arguments.length) { + z = extentDomain || extent; + if (x) { + x0 = z[0][0], x1 = z[1][0]; + if (!extentDomain) { + x0 = extent[0][0], x1 = extent[1][0]; + if (x.invert) x0 = x.invert(x0), x1 = x.invert(x1); + if (x1 < x0) t = x0, x0 = x1, x1 = t; + } + } + if (y) { + y0 = z[0][1], y1 = z[1][1]; + if (!extentDomain) { + y0 = extent[0][1], y1 = extent[1][1]; + if (y.invert) y0 = y.invert(y0), y1 = y.invert(y1); + if (y1 < y0) t = y0, y0 = y1, y1 = t; + } + } + return x && y ? [ [ x0, y0 ], [ x1, y1 ] ] : x ? [ x0, x1 ] : y && [ y0, y1 ]; + } + extentDomain = [ [ 0, 0 ], [ 0, 0 ] ]; + if (x) { + x0 = z[0], x1 = z[1]; + if (y) x0 = x0[0], x1 = x1[0]; + extentDomain[0][0] = x0, extentDomain[1][0] = x1; + if (x.invert) x0 = x(x0), x1 = x(x1); + if (x1 < x0) t = x0, x0 = x1, x1 = t; + extent[0][0] = x0 | 0, extent[1][0] = x1 | 0; + } + if (y) { + y0 = z[0], y1 = z[1]; + if (x) y0 = y0[1], y1 = y1[1]; + extentDomain[0][1] = y0, extentDomain[1][1] = y1; + if (y.invert) y0 = y(y0), y1 = y(y1); + if (y1 < y0) t = y0, y0 = y1, y1 = t; + extent[0][1] = y0 | 0, extent[1][1] = y1 | 0; + } + return brush; + }; + brush.clear = function() { + extentDomain = null; + extent[0][0] = extent[0][1] = extent[1][0] = extent[1][1] = 0; + return brush; + }; + brush.empty = function() { + return x && extent[0][0] === extent[1][0] || y && extent[0][1] === extent[1][1]; + }; + return d3.rebind(brush, event, "on"); + }; + var d3_svg_brushCursor = { + n: "ns-resize", + e: "ew-resize", + s: "ns-resize", + w: "ew-resize", + nw: "nwse-resize", + ne: "nesw-resize", + se: "nwse-resize", + sw: "nesw-resize" + }; + var d3_svg_brushResizes = [ [ "n", "e", "s", "w", "nw", "ne", "se", "sw" ], [ "e", "w" ], [ "n", "s" ], [] ]; + d3.behavior = {}; + d3.behavior.drag = function() { + var event = d3_eventDispatch(drag, "drag", "dragstart", "dragend"), origin = null; + function drag() { + this.on("mousedown.drag", mousedown).on("touchstart.drag", mousedown); + } + function mousedown() { + var target = this, event_ = event.of(target, arguments), eventTarget = d3.event.target, touchId = d3.event.touches ? d3.event.changedTouches[0].identifier : null, offset, origin_ = point(), moved = 0; + var w = d3.select(d3_window).on(touchId != null ? "touchmove.drag-" + touchId : "mousemove.drag", dragmove).on(touchId != null ? "touchend.drag-" + touchId : "mouseup.drag", dragend, true); + if (origin) { + offset = origin.apply(target, arguments); + offset = [ offset.x - origin_[0], offset.y - origin_[1] ]; + } else { + offset = [ 0, 0 ]; + } + if (touchId == null) d3_eventCancel(); + event_({ + type: "dragstart" + }); + function point() { + var p = target.parentNode; + return touchId != null ? d3.touches(p).filter(function(p) { + return p.identifier === touchId; + })[0] : d3.mouse(p); + } + function dragmove() { + if (!target.parentNode) return dragend(); + var p = point(), dx = p[0] - origin_[0], dy = p[1] - origin_[1]; + moved |= dx | dy; + origin_ = p; + d3_eventCancel(); + event_({ + type: "drag", + x: p[0] + offset[0], + y: p[1] + offset[1], + dx: dx, + dy: dy + }); + } + function dragend() { + event_({ + type: "dragend" + }); + if (moved) { + d3_eventCancel(); + if (d3.event.target === eventTarget) w.on("click.drag", click, true); + } + w.on(touchId != null ? "touchmove.drag-" + touchId : "mousemove.drag", null).on(touchId != null ? "touchend.drag-" + touchId : "mouseup.drag", null); + } + function click() { + d3_eventCancel(); + w.on("click.drag", null); + } + } + drag.origin = function(x) { + if (!arguments.length) return origin; + origin = x; + return drag; + }; + return d3.rebind(drag, event, "on"); + }; + d3.behavior.zoom = function() { + var translate = [ 0, 0 ], translate0, scale = 1, scale0, scaleExtent = d3_behavior_zoomInfinity, event = d3_eventDispatch(zoom, "zoom"), x0, x1, y0, y1, touchtime; + function zoom() { + this.on("mousedown.zoom", mousedown).on("mousemove.zoom", mousemove).on(d3_behavior_zoomWheel + ".zoom", mousewheel).on("dblclick.zoom", dblclick).on("touchstart.zoom", touchstart).on("touchmove.zoom", touchmove).on("touchend.zoom", touchstart); + } + zoom.translate = function(x) { + if (!arguments.length) return translate; + translate = x.map(Number); + rescale(); + return zoom; + }; + zoom.scale = function(x) { + if (!arguments.length) return scale; + scale = +x; + rescale(); + return zoom; + }; + zoom.scaleExtent = function(x) { + if (!arguments.length) return scaleExtent; + scaleExtent = x == null ? d3_behavior_zoomInfinity : x.map(Number); + return zoom; + }; + zoom.x = function(z) { + if (!arguments.length) return x1; + x1 = z; + x0 = z.copy(); + translate = [ 0, 0 ]; + scale = 1; + return zoom; + }; + zoom.y = function(z) { + if (!arguments.length) return y1; + y1 = z; + y0 = z.copy(); + translate = [ 0, 0 ]; + scale = 1; + return zoom; + }; + function location(p) { + return [ (p[0] - translate[0]) / scale, (p[1] - translate[1]) / scale ]; + } + function point(l) { + return [ l[0] * scale + translate[0], l[1] * scale + translate[1] ]; + } + function scaleTo(s) { + scale = Math.max(scaleExtent[0], Math.min(scaleExtent[1], s)); + } + function translateTo(p, l) { + l = point(l); + translate[0] += p[0] - l[0]; + translate[1] += p[1] - l[1]; + } + function rescale() { + if (x1) x1.domain(x0.range().map(function(x) { + return (x - translate[0]) / scale; + }).map(x0.invert)); + if (y1) y1.domain(y0.range().map(function(y) { + return (y - translate[1]) / scale; + }).map(y0.invert)); + } + function dispatch(event) { + rescale(); + d3.event.preventDefault(); + event({ + type: "zoom", + scale: scale, + translate: translate + }); + } + function mousedown() { + var target = this, event_ = event.of(target, arguments), eventTarget = d3.event.target, moved = 0, w = d3.select(d3_window).on("mousemove.zoom", mousemove).on("mouseup.zoom", mouseup), l = location(d3.mouse(target)); + d3_window.focus(); + d3_eventCancel(); + function mousemove() { + moved = 1; + translateTo(d3.mouse(target), l); + dispatch(event_); + } + function mouseup() { + if (moved) d3_eventCancel(); + w.on("mousemove.zoom", null).on("mouseup.zoom", null); + if (moved && d3.event.target === eventTarget) w.on("click.zoom", click, true); + } + function click() { + d3_eventCancel(); + w.on("click.zoom", null); + } + } + function mousewheel() { + if (!translate0) translate0 = location(d3.mouse(this)); + scaleTo(Math.pow(2, d3_behavior_zoomDelta() * .002) * scale); + translateTo(d3.mouse(this), translate0); + dispatch(event.of(this, arguments)); + } + function mousemove() { + translate0 = null; + } + function dblclick() { + var p = d3.mouse(this), l = location(p), k = Math.log(scale) / Math.LN2; + scaleTo(Math.pow(2, d3.event.shiftKey ? Math.ceil(k) - 1 : Math.floor(k) + 1)); + translateTo(p, l); + dispatch(event.of(this, arguments)); + } + function touchstart() { + var touches = d3.touches(this), now = Date.now(); + scale0 = scale; + translate0 = {}; + touches.forEach(function(t) { + translate0[t.identifier] = location(t); + }); + d3_eventCancel(); + if (touches.length === 1) { + if (now - touchtime < 500) { + var p = touches[0], l = location(touches[0]); + scaleTo(scale * 2); + translateTo(p, l); + dispatch(event.of(this, arguments)); + } + touchtime = now; + } + } + function touchmove() { + var touches = d3.touches(this), p0 = touches[0], l0 = translate0[p0.identifier]; + if (p1 = touches[1]) { + var p1, l1 = translate0[p1.identifier]; + p0 = [ (p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2 ]; + l0 = [ (l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2 ]; + scaleTo(d3.event.scale * scale0); + } + translateTo(p0, l0); + touchtime = null; + dispatch(event.of(this, arguments)); + } + return d3.rebind(zoom, event, "on"); + }; + var d3_behavior_zoomInfinity = [ 0, Infinity ]; + var d3_behavior_zoomDelta, d3_behavior_zoomWheel = "onwheel" in document ? (d3_behavior_zoomDelta = function() { + return -d3.event.deltaY * (d3.event.deltaMode ? 120 : 1); + }, "wheel") : "onmousewheel" in document ? (d3_behavior_zoomDelta = function() { + return d3.event.wheelDelta; + }, "mousewheel") : (d3_behavior_zoomDelta = function() { + return -d3.event.detail; + }, "MozMousePixelScroll"); + d3.layout = {}; + d3.layout.bundle = function() { + return function(links) { + var paths = [], i = -1, n = links.length; + while (++i < n) paths.push(d3_layout_bundlePath(links[i])); + return paths; + }; + }; + function d3_layout_bundlePath(link) { + var start = link.source, end = link.target, lca = d3_layout_bundleLeastCommonAncestor(start, end), points = [ start ]; + while (start !== lca) { + start = start.parent; + points.push(start); + } + var k = points.length; + while (end !== lca) { + points.splice(k, 0, end); + end = end.parent; + } + return points; + } + function d3_layout_bundleAncestors(node) { + var ancestors = [], parent = node.parent; + while (parent != null) { + ancestors.push(node); + node = parent; + parent = parent.parent; + } + ancestors.push(node); + return ancestors; + } + function d3_layout_bundleLeastCommonAncestor(a, b) { + if (a === b) return a; + var aNodes = d3_layout_bundleAncestors(a), bNodes = d3_layout_bundleAncestors(b), aNode = aNodes.pop(), bNode = bNodes.pop(), sharedNode = null; + while (aNode === bNode) { + sharedNode = aNode; + aNode = aNodes.pop(); + bNode = bNodes.pop(); + } + return sharedNode; + } + d3.layout.chord = function() { + var chord = {}, chords, groups, matrix, n, padding = 0, sortGroups, sortSubgroups, sortChords; + function relayout() { + var subgroups = {}, groupSums = [], groupIndex = d3.range(n), subgroupIndex = [], k, x, x0, i, j; + chords = []; + groups = []; + k = 0, i = -1; + while (++i < n) { + x = 0, j = -1; + while (++j < n) { + x += matrix[i][j]; + } + groupSums.push(x); + subgroupIndex.push(d3.range(n)); + k += x; + } + if (sortGroups) { + groupIndex.sort(function(a, b) { + return sortGroups(groupSums[a], groupSums[b]); + }); + } + if (sortSubgroups) { + subgroupIndex.forEach(function(d, i) { + d.sort(function(a, b) { + return sortSubgroups(matrix[i][a], matrix[i][b]); + }); + }); + } + k = (2 * π - padding * n) / k; + x = 0, i = -1; + while (++i < n) { + x0 = x, j = -1; + while (++j < n) { + var di = groupIndex[i], dj = subgroupIndex[di][j], v = matrix[di][dj], a0 = x, a1 = x += v * k; + subgroups[di + "-" + dj] = { + index: di, + subindex: dj, + startAngle: a0, + endAngle: a1, + value: v + }; + } + groups[di] = { + index: di, + startAngle: x0, + endAngle: x, + value: (x - x0) / k + }; + x += padding; + } + i = -1; + while (++i < n) { + j = i - 1; + while (++j < n) { + var source = subgroups[i + "-" + j], target = subgroups[j + "-" + i]; + if (source.value || target.value) { + chords.push(source.value < target.value ? { + source: target, + target: source + } : { + source: source, + target: target + }); + } + } + } + if (sortChords) resort(); + } + function resort() { + chords.sort(function(a, b) { + return sortChords((a.source.value + a.target.value) / 2, (b.source.value + b.target.value) / 2); + }); + } + chord.matrix = function(x) { + if (!arguments.length) return matrix; + n = (matrix = x) && matrix.length; + chords = groups = null; + return chord; + }; + chord.padding = function(x) { + if (!arguments.length) return padding; + padding = x; + chords = groups = null; + return chord; + }; + chord.sortGroups = function(x) { + if (!arguments.length) return sortGroups; + sortGroups = x; + chords = groups = null; + return chord; + }; + chord.sortSubgroups = function(x) { + if (!arguments.length) return sortSubgroups; + sortSubgroups = x; + chords = null; + return chord; + }; + chord.sortChords = function(x) { + if (!arguments.length) return sortChords; + sortChords = x; + if (chords) resort(); + return chord; + }; + chord.chords = function() { + if (!chords) relayout(); + return chords; + }; + chord.groups = function() { + if (!groups) relayout(); + return groups; + }; + return chord; + }; + d3.layout.force = function() { + var force = {}, event = d3.dispatch("start", "tick", "end"), size = [ 1, 1 ], drag, alpha, friction = .9, linkDistance = d3_layout_forceLinkDistance, linkStrength = d3_layout_forceLinkStrength, charge = -30, gravity = .1, theta = .8, nodes = [], links = [], distances, strengths, charges; + function repulse(node) { + return function(quad, x1, _, x2) { + if (quad.point !== node) { + var dx = quad.cx - node.x, dy = quad.cy - node.y, dn = 1 / Math.sqrt(dx * dx + dy * dy); + if ((x2 - x1) * dn < theta) { + var k = quad.charge * dn * dn; + node.px -= dx * k; + node.py -= dy * k; + return true; + } + if (quad.point && isFinite(dn)) { + var k = quad.pointCharge * dn * dn; + node.px -= dx * k; + node.py -= dy * k; + } + } + return !quad.charge; + }; + } + force.tick = function() { + if ((alpha *= .99) < .005) { + event.end({ + type: "end", + alpha: alpha = 0 + }); + return true; + } + var n = nodes.length, m = links.length, q, i, o, s, t, l, k, x, y; + for (i = 0; i < m; ++i) { + o = links[i]; + s = o.source; + t = o.target; + x = t.x - s.x; + y = t.y - s.y; + if (l = x * x + y * y) { + l = alpha * strengths[i] * ((l = Math.sqrt(l)) - distances[i]) / l; + x *= l; + y *= l; + t.x -= x * (k = s.weight / (t.weight + s.weight)); + t.y -= y * k; + s.x += x * (k = 1 - k); + s.y += y * k; + } + } + if (k = alpha * gravity) { + x = size[0] / 2; + y = size[1] / 2; + i = -1; + if (k) while (++i < n) { + o = nodes[i]; + o.x += (x - o.x) * k; + o.y += (y - o.y) * k; + } + } + if (charge) { + d3_layout_forceAccumulate(q = d3.geom.quadtree(nodes), alpha, charges); + i = -1; + while (++i < n) { + if (!(o = nodes[i]).fixed) { + q.visit(repulse(o)); + } + } + } + i = -1; + while (++i < n) { + o = nodes[i]; + if (o.fixed) { + o.x = o.px; + o.y = o.py; + } else { + o.x -= (o.px - (o.px = o.x)) * friction; + o.y -= (o.py - (o.py = o.y)) * friction; + } + } + event.tick({ + type: "tick", + alpha: alpha + }); + }; + force.nodes = function(x) { + if (!arguments.length) return nodes; + nodes = x; + return force; + }; + force.links = function(x) { + if (!arguments.length) return links; + links = x; + return force; + }; + force.size = function(x) { + if (!arguments.length) return size; + size = x; + return force; + }; + force.linkDistance = function(x) { + if (!arguments.length) return linkDistance; + linkDistance = typeof x === "function" ? x : +x; + return force; + }; + force.distance = force.linkDistance; + force.linkStrength = function(x) { + if (!arguments.length) return linkStrength; + linkStrength = typeof x === "function" ? x : +x; + return force; + }; + force.friction = function(x) { + if (!arguments.length) return friction; + friction = +x; + return force; + }; + force.charge = function(x) { + if (!arguments.length) return charge; + charge = typeof x === "function" ? x : +x; + return force; + }; + force.gravity = function(x) { + if (!arguments.length) return gravity; + gravity = +x; + return force; + }; + force.theta = function(x) { + if (!arguments.length) return theta; + theta = +x; + return force; + }; + force.alpha = function(x) { + if (!arguments.length) return alpha; + x = +x; + if (alpha) { + if (x > 0) alpha = x; else alpha = 0; + } else if (x > 0) { + event.start({ + type: "start", + alpha: alpha = x + }); + d3.timer(force.tick); + } + return force; + }; + force.start = function() { + var i, j, n = nodes.length, m = links.length, w = size[0], h = size[1], neighbors, o; + for (i = 0; i < n; ++i) { + (o = nodes[i]).index = i; + o.weight = 0; + } + for (i = 0; i < m; ++i) { + o = links[i]; + if (typeof o.source == "number") o.source = nodes[o.source]; + if (typeof o.target == "number") o.target = nodes[o.target]; + ++o.source.weight; + ++o.target.weight; + } + for (i = 0; i < n; ++i) { + o = nodes[i]; + if (isNaN(o.x)) o.x = position("x", w); + if (isNaN(o.y)) o.y = position("y", h); + if (isNaN(o.px)) o.px = o.x; + if (isNaN(o.py)) o.py = o.y; + } + distances = []; + if (typeof linkDistance === "function") for (i = 0; i < m; ++i) distances[i] = +linkDistance.call(this, links[i], i); else for (i = 0; i < m; ++i) distances[i] = linkDistance; + strengths = []; + if (typeof linkStrength === "function") for (i = 0; i < m; ++i) strengths[i] = +linkStrength.call(this, links[i], i); else for (i = 0; i < m; ++i) strengths[i] = linkStrength; + charges = []; + if (typeof charge === "function") for (i = 0; i < n; ++i) charges[i] = +charge.call(this, nodes[i], i); else for (i = 0; i < n; ++i) charges[i] = charge; + function position(dimension, size) { + var neighbors = neighbor(i), j = -1, m = neighbors.length, x; + while (++j < m) if (!isNaN(x = neighbors[j][dimension])) return x; + return Math.random() * size; + } + function neighbor() { + if (!neighbors) { + neighbors = []; + for (j = 0; j < n; ++j) { + neighbors[j] = []; + } + for (j = 0; j < m; ++j) { + var o = links[j]; + neighbors[o.source.index].push(o.target); + neighbors[o.target.index].push(o.source); + } + } + return neighbors[i]; + } + return force.resume(); + }; + force.resume = function() { + return force.alpha(.1); + }; + force.stop = function() { + return force.alpha(0); + }; + force.drag = function() { + if (!drag) drag = d3.behavior.drag().origin(d3_identity).on("dragstart.force", d3_layout_forceDragstart).on("drag.force", dragmove).on("dragend.force", d3_layout_forceDragend); + if (!arguments.length) return drag; + this.on("mouseover.force", d3_layout_forceMouseover).on("mouseout.force", d3_layout_forceMouseout).call(drag); + }; + function dragmove(d) { + d.px = d3.event.x, d.py = d3.event.y; + force.resume(); + } + return d3.rebind(force, event, "on"); + }; + function d3_layout_forceDragstart(d) { + d.fixed |= 2; + } + function d3_layout_forceDragend(d) { + d.fixed &= ~6; + } + function d3_layout_forceMouseover(d) { + d.fixed |= 4; + d.px = d.x, d.py = d.y; + } + function d3_layout_forceMouseout(d) { + d.fixed &= ~4; + } + function d3_layout_forceAccumulate(quad, alpha, charges) { + var cx = 0, cy = 0; + quad.charge = 0; + if (!quad.leaf) { + var nodes = quad.nodes, n = nodes.length, i = -1, c; + while (++i < n) { + c = nodes[i]; + if (c == null) continue; + d3_layout_forceAccumulate(c, alpha, charges); + quad.charge += c.charge; + cx += c.charge * c.cx; + cy += c.charge * c.cy; + } + } + if (quad.point) { + if (!quad.leaf) { + quad.point.x += Math.random() - .5; + quad.point.y += Math.random() - .5; + } + var k = alpha * charges[quad.point.index]; + quad.charge += quad.pointCharge = k; + cx += k * quad.point.x; + cy += k * quad.point.y; + } + quad.cx = cx / quad.charge; + quad.cy = cy / quad.charge; + } + var d3_layout_forceLinkDistance = 20, d3_layout_forceLinkStrength = 1; + d3.layout.partition = function() { + var hierarchy = d3.layout.hierarchy(), size = [ 1, 1 ]; + function position(node, x, dx, dy) { + var children = node.children; + node.x = x; + node.y = node.depth * dy; + node.dx = dx; + node.dy = dy; + if (children && (n = children.length)) { + var i = -1, n, c, d; + dx = node.value ? dx / node.value : 0; + while (++i < n) { + position(c = children[i], x, d = c.value * dx, dy); + x += d; + } + } + } + function depth(node) { + var children = node.children, d = 0; + if (children && (n = children.length)) { + var i = -1, n; + while (++i < n) d = Math.max(d, depth(children[i])); + } + return 1 + d; + } + function partition(d, i) { + var nodes = hierarchy.call(this, d, i); + position(nodes[0], 0, size[0], size[1] / depth(nodes[0])); + return nodes; + } + partition.size = function(x) { + if (!arguments.length) return size; + size = x; + return partition; + }; + return d3_layout_hierarchyRebind(partition, hierarchy); + }; + d3.layout.pie = function() { + var value = Number, sort = d3_layout_pieSortByValue, startAngle = 0, endAngle = 2 * π; + function pie(data) { + var values = data.map(function(d, i) { + return +value.call(pie, d, i); + }); + var a = +(typeof startAngle === "function" ? startAngle.apply(this, arguments) : startAngle); + var k = ((typeof endAngle === "function" ? endAngle.apply(this, arguments) : endAngle) - startAngle) / d3.sum(values); + var index = d3.range(data.length); + if (sort != null) index.sort(sort === d3_layout_pieSortByValue ? function(i, j) { + return values[j] - values[i]; + } : function(i, j) { + return sort(data[i], data[j]); + }); + var arcs = []; + index.forEach(function(i) { + var d; + arcs[i] = { + data: data[i], + value: d = values[i], + startAngle: a, + endAngle: a += d * k + }; + }); + return arcs; + } + pie.value = function(x) { + if (!arguments.length) return value; + value = x; + return pie; + }; + pie.sort = function(x) { + if (!arguments.length) return sort; + sort = x; + return pie; + }; + pie.startAngle = function(x) { + if (!arguments.length) return startAngle; + startAngle = x; + return pie; + }; + pie.endAngle = function(x) { + if (!arguments.length) return endAngle; + endAngle = x; + return pie; + }; + return pie; + }; + var d3_layout_pieSortByValue = {}; + d3.layout.stack = function() { + var values = d3_identity, order = d3_layout_stackOrderDefault, offset = d3_layout_stackOffsetZero, out = d3_layout_stackOut, x = d3_layout_stackX, y = d3_layout_stackY; + function stack(data, index) { + var series = data.map(function(d, i) { + return values.call(stack, d, i); + }); + var points = series.map(function(d) { + return d.map(function(v, i) { + return [ x.call(stack, v, i), y.call(stack, v, i) ]; + }); + }); + var orders = order.call(stack, points, index); + series = d3.permute(series, orders); + points = d3.permute(points, orders); + var offsets = offset.call(stack, points, index); + var n = series.length, m = series[0].length, i, j, o; + for (j = 0; j < m; ++j) { + out.call(stack, series[0][j], o = offsets[j], points[0][j][1]); + for (i = 1; i < n; ++i) { + out.call(stack, series[i][j], o += points[i - 1][j][1], points[i][j][1]); + } + } + return data; + } + stack.values = function(x) { + if (!arguments.length) return values; + values = x; + return stack; + }; + stack.order = function(x) { + if (!arguments.length) return order; + order = typeof x === "function" ? x : d3_layout_stackOrders.get(x) || d3_layout_stackOrderDefault; + return stack; + }; + stack.offset = function(x) { + if (!arguments.length) return offset; + offset = typeof x === "function" ? x : d3_layout_stackOffsets.get(x) || d3_layout_stackOffsetZero; + return stack; + }; + stack.x = function(z) { + if (!arguments.length) return x; + x = z; + return stack; + }; + stack.y = function(z) { + if (!arguments.length) return y; + y = z; + return stack; + }; + stack.out = function(z) { + if (!arguments.length) return out; + out = z; + return stack; + }; + return stack; + }; + function d3_layout_stackX(d) { + return d.x; + } + function d3_layout_stackY(d) { + return d.y; + } + function d3_layout_stackOut(d, y0, y) { + d.y0 = y0; + d.y = y; + } + var d3_layout_stackOrders = d3.map({ + "inside-out": function(data) { + var n = data.length, i, j, max = data.map(d3_layout_stackMaxIndex), sums = data.map(d3_layout_stackReduceSum), index = d3.range(n).sort(function(a, b) { + return max[a] - max[b]; + }), top = 0, bottom = 0, tops = [], bottoms = []; + for (i = 0; i < n; ++i) { + j = index[i]; + if (top < bottom) { + top += sums[j]; + tops.push(j); + } else { + bottom += sums[j]; + bottoms.push(j); + } + } + return bottoms.reverse().concat(tops); + }, + reverse: function(data) { + return d3.range(data.length).reverse(); + }, + "default": d3_layout_stackOrderDefault + }); + var d3_layout_stackOffsets = d3.map({ + silhouette: function(data) { + var n = data.length, m = data[0].length, sums = [], max = 0, i, j, o, y0 = []; + for (j = 0; j < m; ++j) { + for (i = 0, o = 0; i < n; i++) o += data[i][j][1]; + if (o > max) max = o; + sums.push(o); + } + for (j = 0; j < m; ++j) { + y0[j] = (max - sums[j]) / 2; + } + return y0; + }, + wiggle: function(data) { + var n = data.length, x = data[0], m = x.length, i, j, k, s1, s2, s3, dx, o, o0, y0 = []; + y0[0] = o = o0 = 0; + for (j = 1; j < m; ++j) { + for (i = 0, s1 = 0; i < n; ++i) s1 += data[i][j][1]; + for (i = 0, s2 = 0, dx = x[j][0] - x[j - 1][0]; i < n; ++i) { + for (k = 0, s3 = (data[i][j][1] - data[i][j - 1][1]) / (2 * dx); k < i; ++k) { + s3 += (data[k][j][1] - data[k][j - 1][1]) / dx; + } + s2 += s3 * data[i][j][1]; + } + y0[j] = o -= s1 ? s2 / s1 * dx : 0; + if (o < o0) o0 = o; + } + for (j = 0; j < m; ++j) y0[j] -= o0; + return y0; + }, + expand: function(data) { + var n = data.length, m = data[0].length, k = 1 / n, i, j, o, y0 = []; + for (j = 0; j < m; ++j) { + for (i = 0, o = 0; i < n; i++) o += data[i][j][1]; + if (o) for (i = 0; i < n; i++) data[i][j][1] /= o; else for (i = 0; i < n; i++) data[i][j][1] = k; + } + for (j = 0; j < m; ++j) y0[j] = 0; + return y0; + }, + zero: d3_layout_stackOffsetZero + }); + function d3_layout_stackOrderDefault(data) { + return d3.range(data.length); + } + function d3_layout_stackOffsetZero(data) { + var j = -1, m = data[0].length, y0 = []; + while (++j < m) y0[j] = 0; + return y0; + } + function d3_layout_stackMaxIndex(array) { + var i = 1, j = 0, v = array[0][1], k, n = array.length; + for (;i < n; ++i) { + if ((k = array[i][1]) > v) { + j = i; + v = k; + } + } + return j; + } + function d3_layout_stackReduceSum(d) { + return d.reduce(d3_layout_stackSum, 0); + } + function d3_layout_stackSum(p, d) { + return p + d[1]; + } + d3.layout.histogram = function() { + var frequency = true, valuer = Number, ranger = d3_layout_histogramRange, binner = d3_layout_histogramBinSturges; + function histogram(data, i) { + var bins = [], values = data.map(valuer, this), range = ranger.call(this, values, i), thresholds = binner.call(this, range, values, i), bin, i = -1, n = values.length, m = thresholds.length - 1, k = frequency ? 1 : 1 / n, x; + while (++i < m) { + bin = bins[i] = []; + bin.dx = thresholds[i + 1] - (bin.x = thresholds[i]); + bin.y = 0; + } + if (m > 0) { + i = -1; + while (++i < n) { + x = values[i]; + if (x >= range[0] && x <= range[1]) { + bin = bins[d3.bisect(thresholds, x, 1, m) - 1]; + bin.y += k; + bin.push(data[i]); + } + } + } + return bins; + } + histogram.value = function(x) { + if (!arguments.length) return valuer; + valuer = x; + return histogram; + }; + histogram.range = function(x) { + if (!arguments.length) return ranger; + ranger = d3_functor(x); + return histogram; + }; + histogram.bins = function(x) { + if (!arguments.length) return binner; + binner = typeof x === "number" ? function(range) { + return d3_layout_histogramBinFixed(range, x); + } : d3_functor(x); + return histogram; + }; + histogram.frequency = function(x) { + if (!arguments.length) return frequency; + frequency = !!x; + return histogram; + }; + return histogram; + }; + function d3_layout_histogramBinSturges(range, values) { + return d3_layout_histogramBinFixed(range, Math.ceil(Math.log(values.length) / Math.LN2 + 1)); + } + function d3_layout_histogramBinFixed(range, n) { + var x = -1, b = +range[0], m = (range[1] - b) / n, f = []; + while (++x <= n) f[x] = m * x + b; + return f; + } + function d3_layout_histogramRange(values) { + return [ d3.min(values), d3.max(values) ]; + } + d3.layout.hierarchy = function() { + var sort = d3_layout_hierarchySort, children = d3_layout_hierarchyChildren, value = d3_layout_hierarchyValue; + function recurse(node, depth, nodes) { + var childs = children.call(hierarchy, node, depth); + node.depth = depth; + nodes.push(node); + if (childs && (n = childs.length)) { + var i = -1, n, c = node.children = [], v = 0, j = depth + 1, d; + while (++i < n) { + d = recurse(childs[i], j, nodes); + d.parent = node; + c.push(d); + v += d.value; + } + if (sort) c.sort(sort); + if (value) node.value = v; + } else if (value) { + node.value = +value.call(hierarchy, node, depth) || 0; + } + return node; + } + function revalue(node, depth) { + var children = node.children, v = 0; + if (children && (n = children.length)) { + var i = -1, n, j = depth + 1; + while (++i < n) v += revalue(children[i], j); + } else if (value) { + v = +value.call(hierarchy, node, depth) || 0; + } + if (value) node.value = v; + return v; + } + function hierarchy(d) { + var nodes = []; + recurse(d, 0, nodes); + return nodes; + } + hierarchy.sort = function(x) { + if (!arguments.length) return sort; + sort = x; + return hierarchy; + }; + hierarchy.children = function(x) { + if (!arguments.length) return children; + children = x; + return hierarchy; + }; + hierarchy.value = function(x) { + if (!arguments.length) return value; + value = x; + return hierarchy; + }; + hierarchy.revalue = function(root) { + revalue(root, 0); + return root; + }; + return hierarchy; + }; + function d3_layout_hierarchyRebind(object, hierarchy) { + d3.rebind(object, hierarchy, "sort", "children", "value"); + object.nodes = object; + object.links = d3_layout_hierarchyLinks; + return object; + } + function d3_layout_hierarchyChildren(d) { + return d.children; + } + function d3_layout_hierarchyValue(d) { + return d.value; + } + function d3_layout_hierarchySort(a, b) { + return b.value - a.value; + } + function d3_layout_hierarchyLinks(nodes) { + return d3.merge(nodes.map(function(parent) { + return (parent.children || []).map(function(child) { + return { + source: parent, + target: child + }; + }); + })); + } + d3.layout.pack = function() { + var hierarchy = d3.layout.hierarchy().sort(d3_layout_packSort), padding = 0, size = [ 1, 1 ]; + function pack(d, i) { + var nodes = hierarchy.call(this, d, i), root = nodes[0]; + root.x = 0; + root.y = 0; + d3_layout_treeVisitAfter(root, function(d) { + d.r = Math.sqrt(d.value); + }); + d3_layout_treeVisitAfter(root, d3_layout_packSiblings); + var w = size[0], h = size[1], k = Math.max(2 * root.r / w, 2 * root.r / h); + if (padding > 0) { + var dr = padding * k / 2; + d3_layout_treeVisitAfter(root, function(d) { + d.r += dr; + }); + d3_layout_treeVisitAfter(root, d3_layout_packSiblings); + d3_layout_treeVisitAfter(root, function(d) { + d.r -= dr; + }); + k = Math.max(2 * root.r / w, 2 * root.r / h); + } + d3_layout_packTransform(root, w / 2, h / 2, 1 / k); + return nodes; + } + pack.size = function(x) { + if (!arguments.length) return size; + size = x; + return pack; + }; + pack.padding = function(_) { + if (!arguments.length) return padding; + padding = +_; + return pack; + }; + return d3_layout_hierarchyRebind(pack, hierarchy); + }; + function d3_layout_packSort(a, b) { + return a.value - b.value; + } + function d3_layout_packInsert(a, b) { + var c = a._pack_next; + a._pack_next = b; + b._pack_prev = a; + b._pack_next = c; + c._pack_prev = b; + } + function d3_layout_packSplice(a, b) { + a._pack_next = b; + b._pack_prev = a; + } + function d3_layout_packIntersects(a, b) { + var dx = b.x - a.x, dy = b.y - a.y, dr = a.r + b.r; + return dr * dr - dx * dx - dy * dy > .001; + } + function d3_layout_packSiblings(node) { + if (!(nodes = node.children) || !(n = nodes.length)) return; + var nodes, xMin = Infinity, xMax = -Infinity, yMin = Infinity, yMax = -Infinity, a, b, c, i, j, k, n; + function bound(node) { + xMin = Math.min(node.x - node.r, xMin); + xMax = Math.max(node.x + node.r, xMax); + yMin = Math.min(node.y - node.r, yMin); + yMax = Math.max(node.y + node.r, yMax); + } + nodes.forEach(d3_layout_packLink); + a = nodes[0]; + a.x = -a.r; + a.y = 0; + bound(a); + if (n > 1) { + b = nodes[1]; + b.x = b.r; + b.y = 0; + bound(b); + if (n > 2) { + c = nodes[2]; + d3_layout_packPlace(a, b, c); + bound(c); + d3_layout_packInsert(a, c); + a._pack_prev = c; + d3_layout_packInsert(c, b); + b = a._pack_next; + for (i = 3; i < n; i++) { + d3_layout_packPlace(a, b, c = nodes[i]); + var isect = 0, s1 = 1, s2 = 1; + for (j = b._pack_next; j !== b; j = j._pack_next, s1++) { + if (d3_layout_packIntersects(j, c)) { + isect = 1; + break; + } + } + if (isect == 1) { + for (k = a._pack_prev; k !== j._pack_prev; k = k._pack_prev, s2++) { + if (d3_layout_packIntersects(k, c)) { + break; + } + } + } + if (isect) { + if (s1 < s2 || s1 == s2 && b.r < a.r) d3_layout_packSplice(a, b = j); else d3_layout_packSplice(a = k, b); + i--; + } else { + d3_layout_packInsert(a, c); + b = c; + bound(c); + } + } + } + } + var cx = (xMin + xMax) / 2, cy = (yMin + yMax) / 2, cr = 0; + for (i = 0; i < n; i++) { + c = nodes[i]; + c.x -= cx; + c.y -= cy; + cr = Math.max(cr, c.r + Math.sqrt(c.x * c.x + c.y * c.y)); + } + node.r = cr; + nodes.forEach(d3_layout_packUnlink); + } + function d3_layout_packLink(node) { + node._pack_next = node._pack_prev = node; + } + function d3_layout_packUnlink(node) { + delete node._pack_next; + delete node._pack_prev; + } + function d3_layout_packTransform(node, x, y, k) { + var children = node.children; + node.x = x += k * node.x; + node.y = y += k * node.y; + node.r *= k; + if (children) { + var i = -1, n = children.length; + while (++i < n) d3_layout_packTransform(children[i], x, y, k); + } + } + function d3_layout_packPlace(a, b, c) { + var db = a.r + c.r, dx = b.x - a.x, dy = b.y - a.y; + if (db && (dx || dy)) { + var da = b.r + c.r, dc = dx * dx + dy * dy; + da *= da; + db *= db; + var x = .5 + (db - da) / (2 * dc), y = Math.sqrt(Math.max(0, 2 * da * (db + dc) - (db -= dc) * db - da * da)) / (2 * dc); + c.x = a.x + x * dx + y * dy; + c.y = a.y + x * dy - y * dx; + } else { + c.x = a.x + db; + c.y = a.y; + } + } + d3.layout.cluster = function() { + var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ]; + function cluster(d, i) { + var nodes = hierarchy.call(this, d, i), root = nodes[0], previousNode, x = 0; + d3_layout_treeVisitAfter(root, function(node) { + var children = node.children; + if (children && children.length) { + node.x = d3_layout_clusterX(children); + node.y = d3_layout_clusterY(children); + } else { + node.x = previousNode ? x += separation(node, previousNode) : 0; + node.y = 0; + previousNode = node; + } + }); + var left = d3_layout_clusterLeft(root), right = d3_layout_clusterRight(root), x0 = left.x - separation(left, right) / 2, x1 = right.x + separation(right, left) / 2; + d3_layout_treeVisitAfter(root, function(node) { + node.x = (node.x - x0) / (x1 - x0) * size[0]; + node.y = (1 - (root.y ? node.y / root.y : 1)) * size[1]; + }); + return nodes; + } + cluster.separation = function(x) { + if (!arguments.length) return separation; + separation = x; + return cluster; + }; + cluster.size = function(x) { + if (!arguments.length) return size; + size = x; + return cluster; + }; + return d3_layout_hierarchyRebind(cluster, hierarchy); + }; + function d3_layout_clusterY(children) { + return 1 + d3.max(children, function(child) { + return child.y; + }); + } + function d3_layout_clusterX(children) { + return children.reduce(function(x, child) { + return x + child.x; + }, 0) / children.length; + } + function d3_layout_clusterLeft(node) { + var children = node.children; + return children && children.length ? d3_layout_clusterLeft(children[0]) : node; + } + function d3_layout_clusterRight(node) { + var children = node.children, n; + return children && (n = children.length) ? d3_layout_clusterRight(children[n - 1]) : node; + } + d3.layout.tree = function() { + var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ]; + function tree(d, i) { + var nodes = hierarchy.call(this, d, i), root = nodes[0]; + function firstWalk(node, previousSibling) { + var children = node.children, layout = node._tree; + if (children && (n = children.length)) { + var n, firstChild = children[0], previousChild, ancestor = firstChild, child, i = -1; + while (++i < n) { + child = children[i]; + firstWalk(child, previousChild); + ancestor = apportion(child, previousChild, ancestor); + previousChild = child; + } + d3_layout_treeShift(node); + var midpoint = .5 * (firstChild._tree.prelim + child._tree.prelim); + if (previousSibling) { + layout.prelim = previousSibling._tree.prelim + separation(node, previousSibling); + layout.mod = layout.prelim - midpoint; + } else { + layout.prelim = midpoint; + } + } else { + if (previousSibling) { + layout.prelim = previousSibling._tree.prelim + separation(node, previousSibling); + } + } + } + function secondWalk(node, x) { + node.x = node._tree.prelim + x; + var children = node.children; + if (children && (n = children.length)) { + var i = -1, n; + x += node._tree.mod; + while (++i < n) { + secondWalk(children[i], x); + } + } + } + function apportion(node, previousSibling, ancestor) { + if (previousSibling) { + var vip = node, vop = node, vim = previousSibling, vom = node.parent.children[0], sip = vip._tree.mod, sop = vop._tree.mod, sim = vim._tree.mod, som = vom._tree.mod, shift; + while (vim = d3_layout_treeRight(vim), vip = d3_layout_treeLeft(vip), vim && vip) { + vom = d3_layout_treeLeft(vom); + vop = d3_layout_treeRight(vop); + vop._tree.ancestor = node; + shift = vim._tree.prelim + sim - vip._tree.prelim - sip + separation(vim, vip); + if (shift > 0) { + d3_layout_treeMove(d3_layout_treeAncestor(vim, node, ancestor), node, shift); + sip += shift; + sop += shift; + } + sim += vim._tree.mod; + sip += vip._tree.mod; + som += vom._tree.mod; + sop += vop._tree.mod; + } + if (vim && !d3_layout_treeRight(vop)) { + vop._tree.thread = vim; + vop._tree.mod += sim - sop; + } + if (vip && !d3_layout_treeLeft(vom)) { + vom._tree.thread = vip; + vom._tree.mod += sip - som; + ancestor = node; + } + } + return ancestor; + } + d3_layout_treeVisitAfter(root, function(node, previousSibling) { + node._tree = { + ancestor: node, + prelim: 0, + mod: 0, + change: 0, + shift: 0, + number: previousSibling ? previousSibling._tree.number + 1 : 0 + }; + }); + firstWalk(root); + secondWalk(root, -root._tree.prelim); + var left = d3_layout_treeSearch(root, d3_layout_treeLeftmost), right = d3_layout_treeSearch(root, d3_layout_treeRightmost), deep = d3_layout_treeSearch(root, d3_layout_treeDeepest), x0 = left.x - separation(left, right) / 2, x1 = right.x + separation(right, left) / 2, y1 = deep.depth || 1; + d3_layout_treeVisitAfter(root, function(node) { + node.x = (node.x - x0) / (x1 - x0) * size[0]; + node.y = node.depth / y1 * size[1]; + delete node._tree; + }); + return nodes; + } + tree.separation = function(x) { + if (!arguments.length) return separation; + separation = x; + return tree; + }; + tree.size = function(x) { + if (!arguments.length) return size; + size = x; + return tree; + }; + return d3_layout_hierarchyRebind(tree, hierarchy); + }; + function d3_layout_treeSeparation(a, b) { + return a.parent == b.parent ? 1 : 2; + } + function d3_layout_treeLeft(node) { + var children = node.children; + return children && children.length ? children[0] : node._tree.thread; + } + function d3_layout_treeRight(node) { + var children = node.children, n; + return children && (n = children.length) ? children[n - 1] : node._tree.thread; + } + function d3_layout_treeSearch(node, compare) { + var children = node.children; + if (children && (n = children.length)) { + var child, n, i = -1; + while (++i < n) { + if (compare(child = d3_layout_treeSearch(children[i], compare), node) > 0) { + node = child; + } + } + } + return node; + } + function d3_layout_treeRightmost(a, b) { + return a.x - b.x; + } + function d3_layout_treeLeftmost(a, b) { + return b.x - a.x; + } + function d3_layout_treeDeepest(a, b) { + return a.depth - b.depth; + } + function d3_layout_treeVisitAfter(node, callback) { + function visit(node, previousSibling) { + var children = node.children; + if (children && (n = children.length)) { + var child, previousChild = null, i = -1, n; + while (++i < n) { + child = children[i]; + visit(child, previousChild); + previousChild = child; + } + } + callback(node, previousSibling); + } + visit(node, null); + } + function d3_layout_treeShift(node) { + var shift = 0, change = 0, children = node.children, i = children.length, child; + while (--i >= 0) { + child = children[i]._tree; + child.prelim += shift; + child.mod += shift; + shift += child.shift + (change += child.change); + } + } + function d3_layout_treeMove(ancestor, node, shift) { + ancestor = ancestor._tree; + node = node._tree; + var change = shift / (node.number - ancestor.number); + ancestor.change += change; + node.change -= change; + node.shift += shift; + node.prelim += shift; + node.mod += shift; + } + function d3_layout_treeAncestor(vim, node, ancestor) { + return vim._tree.ancestor.parent == node.parent ? vim._tree.ancestor : ancestor; + } + d3.layout.treemap = function() { + var hierarchy = d3.layout.hierarchy(), round = Math.round, size = [ 1, 1 ], padding = null, pad = d3_layout_treemapPadNull, sticky = false, stickies, mode = "squarify", ratio = .5 * (1 + Math.sqrt(5)); + function scale(children, k) { + var i = -1, n = children.length, child, area; + while (++i < n) { + area = (child = children[i]).value * (k < 0 ? 0 : k); + child.area = isNaN(area) || area <= 0 ? 0 : area; + } + } + function squarify(node) { + var children = node.children; + if (children && children.length) { + var rect = pad(node), row = [], remaining = children.slice(), child, best = Infinity, score, u = mode === "slice" ? rect.dx : mode === "dice" ? rect.dy : mode === "slice-dice" ? node.depth & 1 ? rect.dy : rect.dx : Math.min(rect.dx, rect.dy), n; + scale(remaining, rect.dx * rect.dy / node.value); + row.area = 0; + while ((n = remaining.length) > 0) { + row.push(child = remaining[n - 1]); + row.area += child.area; + if (mode !== "squarify" || (score = worst(row, u)) <= best) { + remaining.pop(); + best = score; + } else { + row.area -= row.pop().area; + position(row, u, rect, false); + u = Math.min(rect.dx, rect.dy); + row.length = row.area = 0; + best = Infinity; + } + } + if (row.length) { + position(row, u, rect, true); + row.length = row.area = 0; + } + children.forEach(squarify); + } + } + function stickify(node) { + var children = node.children; + if (children && children.length) { + var rect = pad(node), remaining = children.slice(), child, row = []; + scale(remaining, rect.dx * rect.dy / node.value); + row.area = 0; + while (child = remaining.pop()) { + row.push(child); + row.area += child.area; + if (child.z != null) { + position(row, child.z ? rect.dx : rect.dy, rect, !remaining.length); + row.length = row.area = 0; + } + } + children.forEach(stickify); + } + } + function worst(row, u) { + var s = row.area, r, rmax = 0, rmin = Infinity, i = -1, n = row.length; + while (++i < n) { + if (!(r = row[i].area)) continue; + if (r < rmin) rmin = r; + if (r > rmax) rmax = r; + } + s *= s; + u *= u; + return s ? Math.max(u * rmax * ratio / s, s / (u * rmin * ratio)) : Infinity; + } + function position(row, u, rect, flush) { + var i = -1, n = row.length, x = rect.x, y = rect.y, v = u ? round(row.area / u) : 0, o; + if (u == rect.dx) { + if (flush || v > rect.dy) v = rect.dy; + while (++i < n) { + o = row[i]; + o.x = x; + o.y = y; + o.dy = v; + x += o.dx = Math.min(rect.x + rect.dx - x, v ? round(o.area / v) : 0); + } + o.z = true; + o.dx += rect.x + rect.dx - x; + rect.y += v; + rect.dy -= v; + } else { + if (flush || v > rect.dx) v = rect.dx; + while (++i < n) { + o = row[i]; + o.x = x; + o.y = y; + o.dx = v; + y += o.dy = Math.min(rect.y + rect.dy - y, v ? round(o.area / v) : 0); + } + o.z = false; + o.dy += rect.y + rect.dy - y; + rect.x += v; + rect.dx -= v; + } + } + function treemap(d) { + var nodes = stickies || hierarchy(d), root = nodes[0]; + root.x = 0; + root.y = 0; + root.dx = size[0]; + root.dy = size[1]; + if (stickies) hierarchy.revalue(root); + scale([ root ], root.dx * root.dy / root.value); + (stickies ? stickify : squarify)(root); + if (sticky) stickies = nodes; + return nodes; + } + treemap.size = function(x) { + if (!arguments.length) return size; + size = x; + return treemap; + }; + treemap.padding = function(x) { + if (!arguments.length) return padding; + function padFunction(node) { + var p = x.call(treemap, node, node.depth); + return p == null ? d3_layout_treemapPadNull(node) : d3_layout_treemapPad(node, typeof p === "number" ? [ p, p, p, p ] : p); + } + function padConstant(node) { + return d3_layout_treemapPad(node, x); + } + var type; + pad = (padding = x) == null ? d3_layout_treemapPadNull : (type = typeof x) === "function" ? padFunction : type === "number" ? (x = [ x, x, x, x ], + padConstant) : padConstant; + return treemap; + }; + treemap.round = function(x) { + if (!arguments.length) return round != Number; + round = x ? Math.round : Number; + return treemap; + }; + treemap.sticky = function(x) { + if (!arguments.length) return sticky; + sticky = x; + stickies = null; + return treemap; + }; + treemap.ratio = function(x) { + if (!arguments.length) return ratio; + ratio = x; + return treemap; + }; + treemap.mode = function(x) { + if (!arguments.length) return mode; + mode = x + ""; + return treemap; + }; + return d3_layout_hierarchyRebind(treemap, hierarchy); + }; + function d3_layout_treemapPadNull(node) { + return { + x: node.x, + y: node.y, + dx: node.dx, + dy: node.dy + }; + } + function d3_layout_treemapPad(node, padding) { + var x = node.x + padding[3], y = node.y + padding[0], dx = node.dx - padding[1] - padding[3], dy = node.dy - padding[0] - padding[2]; + if (dx < 0) { + x += dx / 2; + dx = 0; + } + if (dy < 0) { + y += dy / 2; + dy = 0; + } + return { + x: x, + y: y, + dx: dx, + dy: dy + }; + } + function d3_dsv(delimiter, mimeType) { + var reFormat = new RegExp('["' + delimiter + "\n]"), delimiterCode = delimiter.charCodeAt(0); + function dsv(url, callback) { + return d3.xhr(url, mimeType, callback).response(response); + } + function response(request) { + return dsv.parse(request.responseText); + } + dsv.parse = function(text) { + var o; + return dsv.parseRows(text, function(row) { + if (o) return o(row); + o = new Function("d", "return {" + row.map(function(name, i) { + return JSON.stringify(name) + ": d[" + i + "]"; + }).join(",") + "}"); + }); + }; + dsv.parseRows = function(text, f) { + var EOL = {}, EOF = {}, rows = [], N = text.length, I = 0, n = 0, t, eol; + function token() { + if (I >= N) return EOF; + if (eol) return eol = false, EOL; + var j = I; + if (text.charCodeAt(j) === 34) { + var i = j; + while (i++ < N) { + if (text.charCodeAt(i) === 34) { + if (text.charCodeAt(i + 1) !== 34) break; + ++i; + } + } + I = i + 2; + var c = text.charCodeAt(i + 1); + if (c === 13) { + eol = true; + if (text.charCodeAt(i + 2) === 10) ++I; + } else if (c === 10) { + eol = true; + } + return text.substring(j + 1, i).replace(/""/g, '"'); + } + while (I < N) { + var c = text.charCodeAt(I++), k = 1; + if (c === 10) eol = true; else if (c === 13) { + eol = true; + if (text.charCodeAt(I) === 10) ++I, ++k; + } else if (c !== delimiterCode) continue; + return text.substring(j, I - k); + } + return text.substring(j); + } + while ((t = token()) !== EOF) { + var a = []; + while (t !== EOL && t !== EOF) { + a.push(t); + t = token(); + } + if (f && !(a = f(a, n++))) continue; + rows.push(a); + } + return rows; + }; + dsv.format = function(rows) { + return rows.map(formatRow).join("\n"); + }; + function formatRow(row) { + return row.map(formatValue).join(delimiter); + } + function formatValue(text) { + return reFormat.test(text) ? '"' + text.replace(/\"/g, '""') + '"' : text; + } + return dsv; + } + d3.csv = d3_dsv(",", "text/csv"); + d3.tsv = d3_dsv(" ", "text/tab-separated-values"); + d3.geo = {}; + d3.geo.stream = function(object, listener) { + if (d3_geo_streamObjectType.hasOwnProperty(object.type)) { + d3_geo_streamObjectType[object.type](object, listener); + } else { + d3_geo_streamGeometry(object, listener); + } + }; + function d3_geo_streamGeometry(geometry, listener) { + if (d3_geo_streamGeometryType.hasOwnProperty(geometry.type)) { + d3_geo_streamGeometryType[geometry.type](geometry, listener); + } + } + var d3_geo_streamObjectType = { + Feature: function(feature, listener) { + d3_geo_streamGeometry(feature.geometry, listener); + }, + FeatureCollection: function(object, listener) { + var features = object.features, i = -1, n = features.length; + while (++i < n) d3_geo_streamGeometry(features[i].geometry, listener); + } + }; + var d3_geo_streamGeometryType = { + Sphere: function(object, listener) { + listener.sphere(); + }, + Point: function(object, listener) { + var coordinate = object.coordinates; + listener.point(coordinate[0], coordinate[1]); + }, + MultiPoint: function(object, listener) { + var coordinates = object.coordinates, i = -1, n = coordinates.length, coordinate; + while (++i < n) coordinate = coordinates[i], listener.point(coordinate[0], coordinate[1]); + }, + LineString: function(object, listener) { + d3_geo_streamLine(object.coordinates, listener, 0); + }, + MultiLineString: function(object, listener) { + var coordinates = object.coordinates, i = -1, n = coordinates.length; + while (++i < n) d3_geo_streamLine(coordinates[i], listener, 0); + }, + Polygon: function(object, listener) { + d3_geo_streamPolygon(object.coordinates, listener); + }, + MultiPolygon: function(object, listener) { + var coordinates = object.coordinates, i = -1, n = coordinates.length; + while (++i < n) d3_geo_streamPolygon(coordinates[i], listener); + }, + GeometryCollection: function(object, listener) { + var geometries = object.geometries, i = -1, n = geometries.length; + while (++i < n) d3_geo_streamGeometry(geometries[i], listener); + } + }; + function d3_geo_streamLine(coordinates, listener, closed) { + var i = -1, n = coordinates.length - closed, coordinate; + listener.lineStart(); + while (++i < n) coordinate = coordinates[i], listener.point(coordinate[0], coordinate[1]); + listener.lineEnd(); + } + function d3_geo_streamPolygon(coordinates, listener) { + var i = -1, n = coordinates.length; + listener.polygonStart(); + while (++i < n) d3_geo_streamLine(coordinates[i], listener, 1); + listener.polygonEnd(); + } + function d3_geo_spherical(cartesian) { + return [ Math.atan2(cartesian[1], cartesian[0]), Math.asin(Math.max(-1, Math.min(1, cartesian[2]))) ]; + } + function d3_geo_sphericalEqual(a, b) { + return Math.abs(a[0] - b[0]) < ε && Math.abs(a[1] - b[1]) < ε; + } + function d3_geo_cartesian(spherical) { + var λ = spherical[0], φ = spherical[1], cosφ = Math.cos(φ); + return [ cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ) ]; + } + function d3_geo_cartesianDot(a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; + } + function d3_geo_cartesianCross(a, b) { + return [ a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0] ]; + } + function d3_geo_cartesianAdd(a, b) { + a[0] += b[0]; + a[1] += b[1]; + a[2] += b[2]; + } + function d3_geo_cartesianScale(vector, k) { + return [ vector[0] * k, vector[1] * k, vector[2] * k ]; + } + function d3_geo_cartesianNormalize(d) { + var l = Math.sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]); + d[0] /= l; + d[1] /= l; + d[2] /= l; + } + function d3_geo_resample(project) { + var δ2 = .5, maxDepth = 16; + function resample(stream) { + var λ0, x0, y0, a0, b0, c0; + var resample = { + point: point, + lineStart: lineStart, + lineEnd: lineEnd, + polygonStart: function() { + stream.polygonStart(); + resample.lineStart = polygonLineStart; + }, + polygonEnd: function() { + stream.polygonEnd(); + resample.lineStart = lineStart; + } + }; + function point(x, y) { + x = project(x, y); + stream.point(x[0], x[1]); + } + function lineStart() { + x0 = NaN; + resample.point = linePoint; + stream.lineStart(); + } + function linePoint(λ, φ) { + var c = d3_geo_cartesian([ λ, φ ]), p = project(λ, φ); + resampleLineTo(x0, y0, λ0, a0, b0, c0, x0 = p[0], y0 = p[1], λ0 = λ, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, stream); + stream.point(x0, y0); + } + function lineEnd() { + resample.point = point; + stream.lineEnd(); + } + function polygonLineStart() { + var λ00, φ00, x00, y00, a00, b00, c00; + lineStart(); + resample.point = function(λ, φ) { + linePoint(λ00 = λ, φ00 = φ), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0; + resample.point = linePoint; + }; + resample.lineEnd = function() { + resampleLineTo(x0, y0, λ0, a0, b0, c0, x00, y00, λ00, a00, b00, c00, maxDepth, stream); + resample.lineEnd = lineEnd; + lineEnd(); + }; + } + return resample; + } + function resampleLineTo(x0, y0, λ0, a0, b0, c0, x1, y1, λ1, a1, b1, c1, depth, stream) { + var dx = x1 - x0, dy = y1 - y0, d2 = dx * dx + dy * dy; + if (d2 > 4 * δ2 && depth--) { + var a = a0 + a1, b = b0 + b1, c = c0 + c1, m = Math.sqrt(a * a + b * b + c * c), φ2 = Math.asin(c /= m), λ2 = Math.abs(Math.abs(c) - 1) < ε ? (λ0 + λ1) / 2 : Math.atan2(b, a), p = project(λ2, φ2), x2 = p[0], y2 = p[1], dx2 = x2 - x0, dy2 = y2 - y0, dz = dy * dx2 - dx * dy2; + if (dz * dz / d2 > δ2 || Math.abs((dx * dx2 + dy * dy2) / d2 - .5) > .3) { + resampleLineTo(x0, y0, λ0, a0, b0, c0, x2, y2, λ2, a /= m, b /= m, c, depth, stream); + stream.point(x2, y2); + resampleLineTo(x2, y2, λ2, a, b, c, x1, y1, λ1, a1, b1, c1, depth, stream); + } + } + } + resample.precision = function(_) { + if (!arguments.length) return Math.sqrt(δ2); + maxDepth = (δ2 = _ * _) > 0 && 16; + return resample; + }; + return resample; + } + d3.geo.albersUsa = function() { + var lower48 = d3.geo.albers(); + var alaska = d3.geo.albers().rotate([ 160, 0 ]).center([ 0, 60 ]).parallels([ 55, 65 ]); + var hawaii = d3.geo.albers().rotate([ 160, 0 ]).center([ 0, 20 ]).parallels([ 8, 18 ]); + var puertoRico = d3.geo.albers().rotate([ 60, 0 ]).center([ 0, 10 ]).parallels([ 8, 18 ]); + function albersUsa(coordinates) { + return projection(coordinates)(coordinates); + } + function projection(point) { + var lon = point[0], lat = point[1]; + return lat > 50 ? alaska : lon < -140 ? hawaii : lat < 21 ? puertoRico : lower48; + } + albersUsa.scale = function(x) { + if (!arguments.length) return lower48.scale(); + lower48.scale(x); + alaska.scale(x * .6); + hawaii.scale(x); + puertoRico.scale(x * 1.5); + return albersUsa.translate(lower48.translate()); + }; + albersUsa.translate = function(x) { + if (!arguments.length) return lower48.translate(); + var dz = lower48.scale(), dx = x[0], dy = x[1]; + lower48.translate(x); + alaska.translate([ dx - .4 * dz, dy + .17 * dz ]); + hawaii.translate([ dx - .19 * dz, dy + .2 * dz ]); + puertoRico.translate([ dx + .58 * dz, dy + .43 * dz ]); + return albersUsa; + }; + return albersUsa.scale(lower48.scale()); + }; + function d3_geo_albers(φ0, φ1) { + var sinφ0 = Math.sin(φ0), n = (sinφ0 + Math.sin(φ1)) / 2, C = 1 + sinφ0 * (2 * n - sinφ0), ρ0 = Math.sqrt(C) / n; + function albers(λ, φ) { + var ρ = Math.sqrt(C - 2 * n * Math.sin(φ)) / n; + return [ ρ * Math.sin(λ *= n), ρ0 - ρ * Math.cos(λ) ]; + } + albers.invert = function(x, y) { + var ρ0_y = ρ0 - y; + return [ Math.atan2(x, ρ0_y) / n, Math.asin((C - (x * x + ρ0_y * ρ0_y) * n * n) / (2 * n)) ]; + }; + return albers; + } + (d3.geo.albers = function() { + var φ0 = 29.5 * d3_radians, φ1 = 45.5 * d3_radians, m = d3_geo_projectionMutator(d3_geo_albers), p = m(φ0, φ1); + p.parallels = function(_) { + if (!arguments.length) return [ φ0 * d3_degrees, φ1 * d3_degrees ]; + return m(φ0 = _[0] * d3_radians, φ1 = _[1] * d3_radians); + }; + return p.rotate([ 98, 0 ]).center([ 0, 38 ]).scale(1e3); + }).raw = d3_geo_albers; + var d3_geo_azimuthalEqualArea = d3_geo_azimuthal(function(cosλcosφ) { + return Math.sqrt(2 / (1 + cosλcosφ)); + }, function(ρ) { + return 2 * Math.asin(ρ / 2); + }); + (d3.geo.azimuthalEqualArea = function() { + return d3_geo_projection(d3_geo_azimuthalEqualArea); + }).raw = d3_geo_azimuthalEqualArea; + var d3_geo_azimuthalEquidistant = d3_geo_azimuthal(function(cosλcosφ) { + var c = Math.acos(cosλcosφ); + return c && c / Math.sin(c); + }, d3_identity); + (d3.geo.azimuthalEquidistant = function() { + return d3_geo_projection(d3_geo_azimuthalEquidistant); + }).raw = d3_geo_azimuthalEquidistant; + d3.geo.bounds = d3_geo_bounds(d3_identity); + function d3_geo_bounds(projectStream) { + var x0, y0, x1, y1; + var bound = { + point: boundPoint, + lineStart: d3_noop, + lineEnd: d3_noop, + polygonStart: function() { + bound.lineEnd = boundPolygonLineEnd; + }, + polygonEnd: function() { + bound.point = boundPoint; + } + }; + function boundPoint(x, y) { + if (x < x0) x0 = x; + if (x > x1) x1 = x; + if (y < y0) y0 = y; + if (y > y1) y1 = y; + } + function boundPolygonLineEnd() { + bound.point = bound.lineEnd = d3_noop; + } + return function(feature) { + y1 = x1 = -(x0 = y0 = Infinity); + d3.geo.stream(feature, projectStream(bound)); + return [ [ x0, y0 ], [ x1, y1 ] ]; + }; + } + d3.geo.centroid = function(object) { + d3_geo_centroidDimension = d3_geo_centroidW = d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0; + d3.geo.stream(object, d3_geo_centroid); + var m; + if (d3_geo_centroidW && Math.abs(m = Math.sqrt(d3_geo_centroidX * d3_geo_centroidX + d3_geo_centroidY * d3_geo_centroidY + d3_geo_centroidZ * d3_geo_centroidZ)) > ε) { + return [ Math.atan2(d3_geo_centroidY, d3_geo_centroidX) * d3_degrees, Math.asin(Math.max(-1, Math.min(1, d3_geo_centroidZ / m))) * d3_degrees ]; + } + }; + var d3_geo_centroidDimension, d3_geo_centroidW, d3_geo_centroidX, d3_geo_centroidY, d3_geo_centroidZ; + var d3_geo_centroid = { + sphere: function() { + if (d3_geo_centroidDimension < 2) { + d3_geo_centroidDimension = 2; + d3_geo_centroidW = d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0; + } + }, + point: d3_geo_centroidPoint, + lineStart: d3_geo_centroidLineStart, + lineEnd: d3_geo_centroidLineEnd, + polygonStart: function() { + if (d3_geo_centroidDimension < 2) { + d3_geo_centroidDimension = 2; + d3_geo_centroidW = d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0; + } + d3_geo_centroid.lineStart = d3_geo_centroidRingStart; + }, + polygonEnd: function() { + d3_geo_centroid.lineStart = d3_geo_centroidLineStart; + } + }; + function d3_geo_centroidPoint(λ, φ) { + if (d3_geo_centroidDimension) return; + ++d3_geo_centroidW; + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians); + d3_geo_centroidX += (cosφ * Math.cos(λ) - d3_geo_centroidX) / d3_geo_centroidW; + d3_geo_centroidY += (cosφ * Math.sin(λ) - d3_geo_centroidY) / d3_geo_centroidW; + d3_geo_centroidZ += (Math.sin(φ) - d3_geo_centroidZ) / d3_geo_centroidW; + } + function d3_geo_centroidRingStart() { + var λ00, φ00; + d3_geo_centroidDimension = 1; + d3_geo_centroidLineStart(); + d3_geo_centroidDimension = 2; + var linePoint = d3_geo_centroid.point; + d3_geo_centroid.point = function(λ, φ) { + linePoint(λ00 = λ, φ00 = φ); + }; + d3_geo_centroid.lineEnd = function() { + d3_geo_centroid.point(λ00, φ00); + d3_geo_centroidLineEnd(); + d3_geo_centroid.lineEnd = d3_geo_centroidLineEnd; + }; + } + function d3_geo_centroidLineStart() { + var x0, y0, z0; + if (d3_geo_centroidDimension > 1) return; + if (d3_geo_centroidDimension < 1) { + d3_geo_centroidDimension = 1; + d3_geo_centroidW = d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0; + } + d3_geo_centroid.point = function(λ, φ) { + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians); + x0 = cosφ * Math.cos(λ); + y0 = cosφ * Math.sin(λ); + z0 = Math.sin(φ); + d3_geo_centroid.point = nextPoint; + }; + function nextPoint(λ, φ) { + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians), x = cosφ * Math.cos(λ), y = cosφ * Math.sin(λ), z = Math.sin(φ), w = Math.atan2(Math.sqrt((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w), x0 * x + y0 * y + z0 * z); + d3_geo_centroidW += w; + d3_geo_centroidX += w * (x0 + (x0 = x)); + d3_geo_centroidY += w * (y0 + (y0 = y)); + d3_geo_centroidZ += w * (z0 + (z0 = z)); + } + } + function d3_geo_centroidLineEnd() { + d3_geo_centroid.point = d3_geo_centroidPoint; + } + d3.geo.circle = function() { + var origin = [ 0, 0 ], angle, precision = 6, interpolate; + function circle() { + var center = typeof origin === "function" ? origin.apply(this, arguments) : origin, rotate = d3_geo_rotation(-center[0] * d3_radians, -center[1] * d3_radians, 0).invert, ring = []; + interpolate(null, null, 1, { + point: function(x, y) { + ring.push(x = rotate(x, y)); + x[0] *= d3_degrees, x[1] *= d3_degrees; + } + }); + return { + type: "Polygon", + coordinates: [ ring ] + }; + } + circle.origin = function(x) { + if (!arguments.length) return origin; + origin = x; + return circle; + }; + circle.angle = function(x) { + if (!arguments.length) return angle; + interpolate = d3_geo_circleInterpolate((angle = +x) * d3_radians, precision * d3_radians); + return circle; + }; + circle.precision = function(_) { + if (!arguments.length) return precision; + interpolate = d3_geo_circleInterpolate(angle * d3_radians, (precision = +_) * d3_radians); + return circle; + }; + return circle.angle(90); + }; + function d3_geo_circleInterpolate(radians, precision) { + var cr = Math.cos(radians), sr = Math.sin(radians); + return function(from, to, direction, listener) { + if (from != null) { + from = d3_geo_circleAngle(cr, from); + to = d3_geo_circleAngle(cr, to); + if (direction > 0 ? from < to : from > to) from += direction * 2 * π; + } else { + from = radians + direction * 2 * π; + to = radians; + } + var point; + for (var step = direction * precision, t = from; direction > 0 ? t > to : t < to; t -= step) { + listener.point((point = d3_geo_spherical([ cr, -sr * Math.cos(t), -sr * Math.sin(t) ]))[0], point[1]); + } + }; + } + function d3_geo_circleAngle(cr, point) { + var a = d3_geo_cartesian(point); + a[0] -= cr; + d3_geo_cartesianNormalize(a); + var angle = Math.acos(Math.max(-1, Math.min(1, -a[1]))); + return ((-a[2] < 0 ? -angle : angle) + 2 * Math.PI - ε) % (2 * Math.PI); + } + function d3_geo_clip(pointVisible, clipLine, interpolate) { + return function(listener) { + var line = clipLine(listener); + var clip = { + point: point, + lineStart: lineStart, + lineEnd: lineEnd, + polygonStart: function() { + clip.point = pointRing; + clip.lineStart = ringStart; + clip.lineEnd = ringEnd; + invisible = false; + invisibleArea = visibleArea = 0; + segments = []; + listener.polygonStart(); + }, + polygonEnd: function() { + clip.point = point; + clip.lineStart = lineStart; + clip.lineEnd = lineEnd; + segments = d3.merge(segments); + if (segments.length) { + d3_geo_clipPolygon(segments, interpolate, listener); + } else if (visibleArea < -ε || invisible && invisibleArea < -ε) { + listener.lineStart(); + interpolate(null, null, 1, listener); + listener.lineEnd(); + } + listener.polygonEnd(); + segments = null; + }, + sphere: function() { + listener.polygonStart(); + listener.lineStart(); + interpolate(null, null, 1, listener); + listener.lineEnd(); + listener.polygonEnd(); + } + }; + function point(λ, φ) { + if (pointVisible(λ, φ)) listener.point(λ, φ); + } + function pointLine(λ, φ) { + line.point(λ, φ); + } + function lineStart() { + clip.point = pointLine; + line.lineStart(); + } + function lineEnd() { + clip.point = point; + line.lineEnd(); + } + var segments, visibleArea, invisibleArea, invisible; + var buffer = d3_geo_clipBufferListener(), ringListener = clipLine(buffer), ring; + function pointRing(λ, φ) { + ringListener.point(λ, φ); + ring.push([ λ, φ ]); + } + function ringStart() { + ringListener.lineStart(); + ring = []; + } + function ringEnd() { + pointRing(ring[0][0], ring[0][1]); + ringListener.lineEnd(); + var clean = ringListener.clean(), ringSegments = buffer.buffer(), segment, n = ringSegments.length; + if (!n) { + invisible = true; + invisibleArea += d3_geo_clipAreaRing(ring, -1); + ring = null; + return; + } + ring = null; + if (clean & 1) { + segment = ringSegments[0]; + visibleArea += d3_geo_clipAreaRing(segment, 1); + var n = segment.length - 1, i = -1, point; + listener.lineStart(); + while (++i < n) listener.point((point = segment[i])[0], point[1]); + listener.lineEnd(); + return; + } + if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift())); + segments.push(ringSegments.filter(d3_geo_clipSegmentLength1)); + } + return clip; + }; + } + function d3_geo_clipPolygon(segments, interpolate, listener) { + var subject = [], clip = []; + segments.forEach(function(segment) { + var n = segment.length; + if (n <= 1) return; + var p0 = segment[0], p1 = segment[n - 1], a = { + point: p0, + points: segment, + other: null, + visited: false, + entry: true, + subject: true + }, b = { + point: p0, + points: [ p0 ], + other: a, + visited: false, + entry: false, + subject: false + }; + a.other = b; + subject.push(a); + clip.push(b); + a = { + point: p1, + points: [ p1 ], + other: null, + visited: false, + entry: false, + subject: true + }; + b = { + point: p1, + points: [ p1 ], + other: a, + visited: false, + entry: true, + subject: false + }; + a.other = b; + subject.push(a); + clip.push(b); + }); + clip.sort(d3_geo_clipSort); + d3_geo_clipLinkCircular(subject); + d3_geo_clipLinkCircular(clip); + if (!subject.length) return; + var start = subject[0], current, points, point; + while (1) { + current = start; + while (current.visited) if ((current = current.next) === start) return; + points = current.points; + listener.lineStart(); + do { + current.visited = current.other.visited = true; + if (current.entry) { + if (current.subject) { + for (var i = 0; i < points.length; i++) listener.point((point = points[i])[0], point[1]); + } else { + interpolate(current.point, current.next.point, 1, listener); + } + current = current.next; + } else { + if (current.subject) { + points = current.prev.points; + for (var i = points.length; --i >= 0; ) listener.point((point = points[i])[0], point[1]); + } else { + interpolate(current.point, current.prev.point, -1, listener); + } + current = current.prev; + } + current = current.other; + points = current.points; + } while (!current.visited); + listener.lineEnd(); + } + } + function d3_geo_clipLinkCircular(array) { + if (!(n = array.length)) return; + var n, i = 0, a = array[0], b; + while (++i < n) { + a.next = b = array[i]; + b.prev = a; + a = b; + } + a.next = b = array[0]; + b.prev = a; + } + function d3_geo_clipSort(a, b) { + return ((a = a.point)[0] < 0 ? a[1] - π / 2 - ε : π / 2 - a[1]) - ((b = b.point)[0] < 0 ? b[1] - π / 2 - ε : π / 2 - b[1]); + } + function d3_geo_clipSegmentLength1(segment) { + return segment.length > 1; + } + function d3_geo_clipBufferListener() { + var lines = [], line; + return { + lineStart: function() { + lines.push(line = []); + }, + point: function(λ, φ) { + line.push([ λ, φ ]); + }, + lineEnd: d3_noop, + buffer: function() { + var buffer = lines; + lines = []; + line = null; + return buffer; + } + }; + } + function d3_geo_clipAreaRing(ring, invisible) { + if (!(n = ring.length)) return 0; + var n, i = 0, area = 0, p = ring[0], λ = p[0], φ = p[1], cosφ = Math.cos(φ), x0 = Math.atan2(invisible * Math.sin(λ) * cosφ, Math.sin(φ)), y0 = 1 - invisible * Math.cos(λ) * cosφ, x1 = x0, x, y; + while (++i < n) { + p = ring[i]; + cosφ = Math.cos(φ = p[1]); + x = Math.atan2(invisible * Math.sin(λ = p[0]) * cosφ, Math.sin(φ)); + y = 1 - invisible * Math.cos(λ) * cosφ; + if (Math.abs(y0 - 2) < ε && Math.abs(y - 2) < ε) continue; + if (Math.abs(y) < ε || Math.abs(y0) < ε) {} else if (Math.abs(Math.abs(x - x0) - π) < ε) { + if (y + y0 > 2) area += 4 * (x - x0); + } else if (Math.abs(y0 - 2) < ε) area += 4 * (x - x1); else area += ((3 * π + x - x0) % (2 * π) - π) * (y0 + y); + x1 = x0, x0 = x, y0 = y; + } + return area; + } + var d3_geo_clipAntimeridian = d3_geo_clip(d3_true, d3_geo_clipAntimeridianLine, d3_geo_clipAntimeridianInterpolate); + function d3_geo_clipAntimeridianLine(listener) { + var λ0 = NaN, φ0 = NaN, sλ0 = NaN, clean; + return { + lineStart: function() { + listener.lineStart(); + clean = 1; + }, + point: function(λ1, φ1) { + var sλ1 = λ1 > 0 ? π : -π, dλ = Math.abs(λ1 - λ0); + if (Math.abs(dλ - π) < ε) { + listener.point(λ0, φ0 = (φ0 + φ1) / 2 > 0 ? π / 2 : -π / 2); + listener.point(sλ0, φ0); + listener.lineEnd(); + listener.lineStart(); + listener.point(sλ1, φ0); + listener.point(λ1, φ0); + clean = 0; + } else if (sλ0 !== sλ1 && dλ >= π) { + if (Math.abs(λ0 - sλ0) < ε) λ0 -= sλ0 * ε; + if (Math.abs(λ1 - sλ1) < ε) λ1 -= sλ1 * ε; + φ0 = d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1); + listener.point(sλ0, φ0); + listener.lineEnd(); + listener.lineStart(); + listener.point(sλ1, φ0); + clean = 0; + } + listener.point(λ0 = λ1, φ0 = φ1); + sλ0 = sλ1; + }, + lineEnd: function() { + listener.lineEnd(); + λ0 = φ0 = NaN; + }, + clean: function() { + return 2 - clean; + } + }; + } + function d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1) { + var cosφ0, cosφ1, sinλ0_λ1 = Math.sin(λ0 - λ1); + return Math.abs(sinλ0_λ1) > ε ? Math.atan((Math.sin(φ0) * (cosφ1 = Math.cos(φ1)) * Math.sin(λ1) - Math.sin(φ1) * (cosφ0 = Math.cos(φ0)) * Math.sin(λ0)) / (cosφ0 * cosφ1 * sinλ0_λ1)) : (φ0 + φ1) / 2; + } + function d3_geo_clipAntimeridianInterpolate(from, to, direction, listener) { + var φ; + if (from == null) { + φ = direction * π / 2; + listener.point(-π, φ); + listener.point(0, φ); + listener.point(π, φ); + listener.point(π, 0); + listener.point(π, -φ); + listener.point(0, -φ); + listener.point(-π, -φ); + listener.point(-π, 0); + listener.point(-π, φ); + } else if (Math.abs(from[0] - to[0]) > ε) { + var s = (from[0] < to[0] ? 1 : -1) * π; + φ = direction * s / 2; + listener.point(-s, φ); + listener.point(0, φ); + listener.point(s, φ); + } else { + listener.point(to[0], to[1]); + } + } + function d3_geo_clipCircle(degrees) { + var radians = degrees * d3_radians, cr = Math.cos(radians), interpolate = d3_geo_circleInterpolate(radians, 6 * d3_radians); + return d3_geo_clip(visible, clipLine, interpolate); + function visible(λ, φ) { + return Math.cos(λ) * Math.cos(φ) > cr; + } + function clipLine(listener) { + var point0, v0, v00, clean; + return { + lineStart: function() { + v00 = v0 = false; + clean = 1; + }, + point: function(λ, φ) { + var point1 = [ λ, φ ], point2, v = visible(λ, φ); + if (!point0 && (v00 = v0 = v)) listener.lineStart(); + if (v !== v0) { + point2 = intersect(point0, point1); + if (d3_geo_sphericalEqual(point0, point2) || d3_geo_sphericalEqual(point1, point2)) { + point1[0] += ε; + point1[1] += ε; + v = visible(point1[0], point1[1]); + } + } + if (v !== v0) { + clean = 0; + if (v0 = v) { + listener.lineStart(); + point2 = intersect(point1, point0); + listener.point(point2[0], point2[1]); + } else { + point2 = intersect(point0, point1); + listener.point(point2[0], point2[1]); + listener.lineEnd(); + } + point0 = point2; + } + if (v && (!point0 || !d3_geo_sphericalEqual(point0, point1))) listener.point(point1[0], point1[1]); + point0 = point1; + }, + lineEnd: function() { + if (v0) listener.lineEnd(); + point0 = null; + }, + clean: function() { + return clean | (v00 && v0) << 1; + } + }; + } + function intersect(a, b) { + var pa = d3_geo_cartesian(a, 0), pb = d3_geo_cartesian(b, 0); + var n1 = [ 1, 0, 0 ], n2 = d3_geo_cartesianCross(pa, pb), n2n2 = d3_geo_cartesianDot(n2, n2), n1n2 = n2[0], determinant = n2n2 - n1n2 * n1n2; + if (!determinant) return a; + var c1 = cr * n2n2 / determinant, c2 = -cr * n1n2 / determinant, n1xn2 = d3_geo_cartesianCross(n1, n2), A = d3_geo_cartesianScale(n1, c1), B = d3_geo_cartesianScale(n2, c2); + d3_geo_cartesianAdd(A, B); + var u = n1xn2, w = d3_geo_cartesianDot(A, u), uu = d3_geo_cartesianDot(u, u), t = Math.sqrt(w * w - uu * (d3_geo_cartesianDot(A, A) - 1)), q = d3_geo_cartesianScale(u, (-w - t) / uu); + d3_geo_cartesianAdd(q, A); + return d3_geo_spherical(q); + } + } + function d3_geo_compose(a, b) { + function compose(x, y) { + return x = a(x, y), b(x[0], x[1]); + } + if (a.invert && b.invert) compose.invert = function(x, y) { + return x = b.invert(x, y), x && a.invert(x[0], x[1]); + }; + return compose; + } + function d3_geo_equirectangular(λ, φ) { + return [ λ, φ ]; + } + (d3.geo.equirectangular = function() { + return d3_geo_projection(d3_geo_equirectangular).scale(250 / π); + }).raw = d3_geo_equirectangular.invert = d3_geo_equirectangular; + var d3_geo_gnomonic = d3_geo_azimuthal(function(cosλcosφ) { + return 1 / cosλcosφ; + }, Math.atan); + (d3.geo.gnomonic = function() { + return d3_geo_projection(d3_geo_gnomonic); + }).raw = d3_geo_gnomonic; + d3.geo.graticule = function() { + var x1, x0, y1, y0, dx = 22.5, dy = dx, x, y, precision = 2.5; + function graticule() { + return { + type: "MultiLineString", + coordinates: lines() + }; + } + function lines() { + return d3.range(Math.ceil(x0 / dx) * dx, x1, dx).map(x).concat(d3.range(Math.ceil(y0 / dy) * dy, y1, dy).map(y)); + } + graticule.lines = function() { + return lines().map(function(coordinates) { + return { + type: "LineString", + coordinates: coordinates + }; + }); + }; + graticule.outline = function() { + return { + type: "Polygon", + coordinates: [ x(x0).concat(y(y1).slice(1), x(x1).reverse().slice(1), y(y0).reverse().slice(1)) ] + }; + }; + graticule.extent = function(_) { + if (!arguments.length) return [ [ x0, y0 ], [ x1, y1 ] ]; + x0 = +_[0][0], x1 = +_[1][0]; + y0 = +_[0][1], y1 = +_[1][1]; + if (x0 > x1) _ = x0, x0 = x1, x1 = _; + if (y0 > y1) _ = y0, y0 = y1, y1 = _; + return graticule.precision(precision); + }; + graticule.step = function(_) { + if (!arguments.length) return [ dx, dy ]; + dx = +_[0], dy = +_[1]; + return graticule; + }; + graticule.precision = function(_) { + if (!arguments.length) return precision; + precision = +_; + x = d3_geo_graticuleX(y0, y1, precision); + y = d3_geo_graticuleY(x0, x1, precision); + return graticule; + }; + return graticule.extent([ [ -180 + ε, -90 + ε ], [ 180 - ε, 90 - ε ] ]); + }; + function d3_geo_graticuleX(y0, y1, dy) { + var y = d3.range(y0, y1 - ε, dy).concat(y1); + return function(x) { + return y.map(function(y) { + return [ x, y ]; + }); + }; + } + function d3_geo_graticuleY(x0, x1, dx) { + var x = d3.range(x0, x1 - ε, dx).concat(x1); + return function(y) { + return x.map(function(x) { + return [ x, y ]; + }); + }; + } + d3.geo.interpolate = function(source, target) { + return d3_geo_interpolate(source[0] * d3_radians, source[1] * d3_radians, target[0] * d3_radians, target[1] * d3_radians); + }; + function d3_geo_interpolate(x0, y0, x1, y1) { + var cy0 = Math.cos(y0), sy0 = Math.sin(y0), cy1 = Math.cos(y1), sy1 = Math.sin(y1), kx0 = cy0 * Math.cos(x0), ky0 = cy0 * Math.sin(x0), kx1 = cy1 * Math.cos(x1), ky1 = cy1 * Math.sin(x1), d = Math.acos(Math.max(-1, Math.min(1, sy0 * sy1 + cy0 * cy1 * Math.cos(x1 - x0)))), k = 1 / Math.sin(d); + function interpolate(t) { + var B = Math.sin(t *= d) * k, A = Math.sin(d - t) * k, x = A * kx0 + B * kx1, y = A * ky0 + B * ky1, z = A * sy0 + B * sy1; + return [ Math.atan2(y, x) / d3_radians, Math.atan2(z, Math.sqrt(x * x + y * y)) / d3_radians ]; + } + interpolate.distance = d; + return interpolate; + } + d3.geo.greatArc = function() { + var source = d3_source, source_, target = d3_target, target_, precision = 6 * d3_radians, interpolate; + function greatArc() { + var p0 = source_ || source.apply(this, arguments), p1 = target_ || target.apply(this, arguments), i = interpolate || d3.geo.interpolate(p0, p1), t = 0, dt = precision / i.distance, coordinates = [ p0 ]; + while ((t += dt) < 1) coordinates.push(i(t)); + coordinates.push(p1); + return { + type: "LineString", + coordinates: coordinates + }; + } + greatArc.distance = function() { + return (interpolate || d3.geo.interpolate(source_ || source.apply(this, arguments), target_ || target.apply(this, arguments))).distance; + }; + greatArc.source = function(_) { + if (!arguments.length) return source; + source = _, source_ = typeof _ === "function" ? null : _; + interpolate = source_ && target_ ? d3.geo.interpolate(source_, target_) : null; + return greatArc; + }; + greatArc.target = function(_) { + if (!arguments.length) return target; + target = _, target_ = typeof _ === "function" ? null : _; + interpolate = source_ && target_ ? d3.geo.interpolate(source_, target_) : null; + return greatArc; + }; + greatArc.precision = function(_) { + if (!arguments.length) return precision / d3_radians; + precision = _ * d3_radians; + return greatArc; + }; + return greatArc; + }; + function d3_geo_mercator(λ, φ) { + return [ λ / (2 * π), Math.max(-.5, Math.min(+.5, Math.log(Math.tan(π / 4 + φ / 2)) / (2 * π))) ]; + } + d3_geo_mercator.invert = function(x, y) { + return [ 2 * π * x, 2 * Math.atan(Math.exp(2 * π * y)) - π / 2 ]; + }; + (d3.geo.mercator = function() { + return d3_geo_projection(d3_geo_mercator).scale(500); + }).raw = d3_geo_mercator; + var d3_geo_orthographic = d3_geo_azimuthal(function() { + return 1; + }, Math.asin); + (d3.geo.orthographic = function() { + return d3_geo_projection(d3_geo_orthographic); + }).raw = d3_geo_orthographic; + d3.geo.path = function() { + var pointRadius = 4.5, projection, context, projectStream, contextStream; + function path(object) { + if (object) d3.geo.stream(object, projectStream(contextStream.pointRadius(typeof pointRadius === "function" ? +pointRadius.apply(this, arguments) : pointRadius))); + return contextStream.result(); + } + path.area = function(object) { + d3_geo_pathAreaSum = 0; + d3.geo.stream(object, projectStream(d3_geo_pathArea)); + return d3_geo_pathAreaSum; + }; + path.centroid = function(object) { + d3_geo_centroidDimension = d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0; + d3.geo.stream(object, projectStream(d3_geo_pathCentroid)); + return d3_geo_centroidZ ? [ d3_geo_centroidX / d3_geo_centroidZ, d3_geo_centroidY / d3_geo_centroidZ ] : undefined; + }; + path.bounds = function(object) { + return d3_geo_bounds(projectStream)(object); + }; + path.projection = function(_) { + if (!arguments.length) return projection; + projectStream = (projection = _) ? _.stream || d3_geo_pathProjectStream(_) : d3_identity; + return path; + }; + path.context = function(_) { + if (!arguments.length) return context; + contextStream = (context = _) == null ? new d3_geo_pathBuffer() : new d3_geo_pathContext(_); + return path; + }; + path.pointRadius = function(_) { + if (!arguments.length) return pointRadius; + pointRadius = typeof _ === "function" ? _ : +_; + return path; + }; + return path.projection(d3.geo.albersUsa()).context(null); + }; + function d3_geo_pathCircle(radius) { + return "m0," + radius + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius + "a" + radius + "," + radius + " 0 1,1 0," + +2 * radius + "z"; + } + function d3_geo_pathProjectStream(project) { + var resample = d3_geo_resample(function(λ, φ) { + return project([ λ * d3_degrees, φ * d3_degrees ]); + }); + return function(stream) { + stream = resample(stream); + return { + point: function(λ, φ) { + stream.point(λ * d3_radians, φ * d3_radians); + }, + sphere: function() { + stream.sphere(); + }, + lineStart: function() { + stream.lineStart(); + }, + lineEnd: function() { + stream.lineEnd(); + }, + polygonStart: function() { + stream.polygonStart(); + }, + polygonEnd: function() { + stream.polygonEnd(); + } + }; + }; + } + function d3_geo_pathBuffer() { + var pointCircle = d3_geo_pathCircle(4.5), buffer = []; + var stream = { + point: point, + lineStart: function() { + stream.point = pointLineStart; + }, + lineEnd: lineEnd, + polygonStart: function() { + stream.lineEnd = lineEndPolygon; + }, + polygonEnd: function() { + stream.lineEnd = lineEnd; + stream.point = point; + }, + pointRadius: function(_) { + pointCircle = d3_geo_pathCircle(_); + return stream; + }, + result: function() { + if (buffer.length) { + var result = buffer.join(""); + buffer = []; + return result; + } + } + }; + function point(x, y) { + buffer.push("M", x, ",", y, pointCircle); + } + function pointLineStart(x, y) { + buffer.push("M", x, ",", y); + stream.point = pointLine; + } + function pointLine(x, y) { + buffer.push("L", x, ",", y); + } + function lineEnd() { + stream.point = point; + } + function lineEndPolygon() { + buffer.push("Z"); + } + return stream; + } + function d3_geo_pathContext(context) { + var pointRadius = 4.5; + var stream = { + point: point, + lineStart: function() { + stream.point = pointLineStart; + }, + lineEnd: lineEnd, + polygonStart: function() { + stream.lineEnd = lineEndPolygon; + }, + polygonEnd: function() { + stream.lineEnd = lineEnd; + stream.point = point; + }, + pointRadius: function(_) { + pointRadius = _; + return stream; + }, + result: d3_noop + }; + function point(x, y) { + context.moveTo(x, y); + context.arc(x, y, pointRadius, 0, 2 * π); + } + function pointLineStart(x, y) { + context.moveTo(x, y); + stream.point = pointLine; + } + function pointLine(x, y) { + context.lineTo(x, y); + } + function lineEnd() { + stream.point = point; + } + function lineEndPolygon() { + context.closePath(); + } + return stream; + } + var d3_geo_pathAreaSum, d3_geo_pathAreaPolygon, d3_geo_pathArea = { + point: d3_noop, + lineStart: d3_noop, + lineEnd: d3_noop, + polygonStart: function() { + d3_geo_pathAreaPolygon = 0; + d3_geo_pathArea.lineStart = d3_geo_pathAreaRingStart; + }, + polygonEnd: function() { + d3_geo_pathArea.lineStart = d3_geo_pathArea.lineEnd = d3_geo_pathArea.point = d3_noop; + d3_geo_pathAreaSum += Math.abs(d3_geo_pathAreaPolygon / 2); + } + }; + function d3_geo_pathAreaRingStart() { + var x00, y00, x0, y0; + d3_geo_pathArea.point = function(x, y) { + d3_geo_pathArea.point = nextPoint; + x00 = x0 = x, y00 = y0 = y; + }; + function nextPoint(x, y) { + d3_geo_pathAreaPolygon += y0 * x - x0 * y; + x0 = x, y0 = y; + } + d3_geo_pathArea.lineEnd = function() { + nextPoint(x00, y00); + }; + } + var d3_geo_pathCentroid = { + point: d3_geo_pathCentroidPoint, + lineStart: d3_geo_pathCentroidLineStart, + lineEnd: d3_geo_pathCentroidLineEnd, + polygonStart: function() { + d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidRingStart; + }, + polygonEnd: function() { + d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint; + d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidLineStart; + d3_geo_pathCentroid.lineEnd = d3_geo_pathCentroidLineEnd; + } + }; + function d3_geo_pathCentroidPoint(x, y) { + if (d3_geo_centroidDimension) return; + d3_geo_centroidX += x; + d3_geo_centroidY += y; + ++d3_geo_centroidZ; + } + function d3_geo_pathCentroidLineStart() { + var x0, y0; + if (d3_geo_centroidDimension !== 1) { + if (d3_geo_centroidDimension < 1) { + d3_geo_centroidDimension = 1; + d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0; + } else return; + } + d3_geo_pathCentroid.point = function(x, y) { + d3_geo_pathCentroid.point = nextPoint; + x0 = x, y0 = y; + }; + function nextPoint(x, y) { + var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy); + d3_geo_centroidX += z * (x0 + x) / 2; + d3_geo_centroidY += z * (y0 + y) / 2; + d3_geo_centroidZ += z; + x0 = x, y0 = y; + } + } + function d3_geo_pathCentroidLineEnd() { + d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint; + } + function d3_geo_pathCentroidRingStart() { + var x00, y00, x0, y0; + if (d3_geo_centroidDimension < 2) { + d3_geo_centroidDimension = 2; + d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0; + } + d3_geo_pathCentroid.point = function(x, y) { + d3_geo_pathCentroid.point = nextPoint; + x00 = x0 = x, y00 = y0 = y; + }; + function nextPoint(x, y) { + var z = y0 * x - x0 * y; + d3_geo_centroidX += z * (x0 + x); + d3_geo_centroidY += z * (y0 + y); + d3_geo_centroidZ += z * 3; + x0 = x, y0 = y; + } + d3_geo_pathCentroid.lineEnd = function() { + nextPoint(x00, y00); + }; + } + d3.geo.area = function(object) { + d3_geo_areaSum = 0; + d3.geo.stream(object, d3_geo_area); + return d3_geo_areaSum; + }; + var d3_geo_areaSum, d3_geo_areaRingU, d3_geo_areaRingV; + var d3_geo_area = { + sphere: function() { + d3_geo_areaSum += 4 * π; + }, + point: d3_noop, + lineStart: d3_noop, + lineEnd: d3_noop, + polygonStart: function() { + d3_geo_areaRingU = 1, d3_geo_areaRingV = 0; + d3_geo_area.lineStart = d3_geo_areaRingStart; + }, + polygonEnd: function() { + var area = 2 * Math.atan2(d3_geo_areaRingV, d3_geo_areaRingU); + d3_geo_areaSum += area < 0 ? 4 * π + area : area; + d3_geo_area.lineStart = d3_geo_area.lineEnd = d3_geo_area.point = d3_noop; + } + }; + function d3_geo_areaRingStart() { + var λ00, φ00, λ0, cosφ0, sinφ0; + d3_geo_area.point = function(λ, φ) { + d3_geo_area.point = nextPoint; + λ0 = (λ00 = λ) * d3_radians, cosφ0 = Math.cos(φ = (φ00 = φ) * d3_radians / 2 + π / 4), + sinφ0 = Math.sin(φ); + }; + function nextPoint(λ, φ) { + λ *= d3_radians; + φ = φ * d3_radians / 2 + π / 4; + var dλ = λ - λ0, cosφ = Math.cos(φ), sinφ = Math.sin(φ), k = sinφ0 * sinφ, u0 = d3_geo_areaRingU, v0 = d3_geo_areaRingV, u = cosφ0 * cosφ + k * Math.cos(dλ), v = k * Math.sin(dλ); + d3_geo_areaRingU = u0 * u - v0 * v; + d3_geo_areaRingV = v0 * u + u0 * v; + λ0 = λ, cosφ0 = cosφ, sinφ0 = sinφ; + } + d3_geo_area.lineEnd = function() { + nextPoint(λ00, φ00); + }; + } + d3.geo.projection = d3_geo_projection; + d3.geo.projectionMutator = d3_geo_projectionMutator; + function d3_geo_projection(project) { + return d3_geo_projectionMutator(function() { + return project; + })(); + } + function d3_geo_projectionMutator(projectAt) { + var project, rotate, projectRotate, projectResample = d3_geo_resample(function(x, y) { + x = project(x, y); + return [ x[0] * k + δx, δy - x[1] * k ]; + }), k = 150, x = 480, y = 250, λ = 0, φ = 0, δλ = 0, δφ = 0, δγ = 0, δx, δy, clip = d3_geo_clipAntimeridian, clipAngle = null; + function projection(point) { + point = projectRotate(point[0] * d3_radians, point[1] * d3_radians); + return [ point[0] * k + δx, δy - point[1] * k ]; + } + function invert(point) { + point = projectRotate.invert((point[0] - δx) / k, (δy - point[1]) / k); + return point && [ point[0] * d3_degrees, point[1] * d3_degrees ]; + } + projection.stream = function(stream) { + return d3_geo_projectionRadiansRotate(rotate, clip(projectResample(stream))); + }; + projection.clipAngle = function(_) { + if (!arguments.length) return clipAngle; + clip = _ == null ? (clipAngle = _, d3_geo_clipAntimeridian) : d3_geo_clipCircle(clipAngle = +_); + return projection; + }; + projection.scale = function(_) { + if (!arguments.length) return k; + k = +_; + return reset(); + }; + projection.translate = function(_) { + if (!arguments.length) return [ x, y ]; + x = +_[0]; + y = +_[1]; + return reset(); + }; + projection.center = function(_) { + if (!arguments.length) return [ λ * d3_degrees, φ * d3_degrees ]; + λ = _[0] % 360 * d3_radians; + φ = _[1] % 360 * d3_radians; + return reset(); + }; + projection.rotate = function(_) { + if (!arguments.length) return [ δλ * d3_degrees, δφ * d3_degrees, δγ * d3_degrees ]; + δλ = _[0] % 360 * d3_radians; + δφ = _[1] % 360 * d3_radians; + δγ = _.length > 2 ? _[2] % 360 * d3_radians : 0; + return reset(); + }; + d3.rebind(projection, projectResample, "precision"); + function reset() { + projectRotate = d3_geo_compose(rotate = d3_geo_rotation(δλ, δφ, δγ), project); + var center = project(λ, φ); + δx = x - center[0] * k; + δy = y + center[1] * k; + return projection; + } + return function() { + project = projectAt.apply(this, arguments); + projection.invert = project.invert && invert; + return reset(); + }; + } + function d3_geo_projectionRadiansRotate(rotate, stream) { + return { + point: function(x, y) { + y = rotate(x * d3_radians, y * d3_radians), x = y[0]; + stream.point(x > π ? x - 2 * π : x < -π ? x + 2 * π : x, y[1]); + }, + sphere: function() { + stream.sphere(); + }, + lineStart: function() { + stream.lineStart(); + }, + lineEnd: function() { + stream.lineEnd(); + }, + polygonStart: function() { + stream.polygonStart(); + }, + polygonEnd: function() { + stream.polygonEnd(); + } + }; + } + function d3_geo_rotation(δλ, δφ, δγ) { + return δλ ? δφ || δγ ? d3_geo_compose(d3_geo_rotationλ(δλ), d3_geo_rotationφγ(δφ, δγ)) : d3_geo_rotationλ(δλ) : δφ || δγ ? d3_geo_rotationφγ(δφ, δγ) : d3_geo_equirectangular; + } + function d3_geo_forwardRotationλ(δλ) { + return function(λ, φ) { + return λ += δλ, [ λ > π ? λ - 2 * π : λ < -π ? λ + 2 * π : λ, φ ]; + }; + } + function d3_geo_rotationλ(δλ) { + var rotation = d3_geo_forwardRotationλ(δλ); + rotation.invert = d3_geo_forwardRotationλ(-δλ); + return rotation; + } + function d3_geo_rotationφγ(δφ, δγ) { + var cosδφ = Math.cos(δφ), sinδφ = Math.sin(δφ), cosδγ = Math.cos(δγ), sinδγ = Math.sin(δγ); + function rotation(λ, φ) { + var cosφ = Math.cos(φ), x = Math.cos(λ) * cosφ, y = Math.sin(λ) * cosφ, z = Math.sin(φ), k = z * cosδφ + x * sinδφ; + return [ Math.atan2(y * cosδγ - k * sinδγ, x * cosδφ - z * sinδφ), Math.asin(Math.max(-1, Math.min(1, k * cosδγ + y * sinδγ))) ]; + } + rotation.invert = function(λ, φ) { + var cosφ = Math.cos(φ), x = Math.cos(λ) * cosφ, y = Math.sin(λ) * cosφ, z = Math.sin(φ), k = z * cosδγ - y * sinδγ; + return [ Math.atan2(y * cosδγ + z * sinδγ, x * cosδφ + k * sinδφ), Math.asin(Math.max(-1, Math.min(1, k * cosδφ - x * sinδφ))) ]; + }; + return rotation; + } + var d3_geo_stereographic = d3_geo_azimuthal(function(cosλcosφ) { + return 1 / (1 + cosλcosφ); + }, function(ρ) { + return 2 * Math.atan(ρ); + }); + (d3.geo.stereographic = function() { + return d3_geo_projection(d3_geo_stereographic); + }).raw = d3_geo_stereographic; + function d3_geo_azimuthal(scale, angle) { + function azimuthal(λ, φ) { + var cosλ = Math.cos(λ), cosφ = Math.cos(φ), k = scale(cosλ * cosφ); + return [ k * cosφ * Math.sin(λ), k * Math.sin(φ) ]; + } + azimuthal.invert = function(x, y) { + var ρ = Math.sqrt(x * x + y * y), c = angle(ρ), sinc = Math.sin(c), cosc = Math.cos(c); + return [ Math.atan2(x * sinc, ρ * cosc), Math.asin(ρ && y * sinc / ρ) ]; + }; + return azimuthal; + } + d3.geom = {}; + d3.geom.hull = function(vertices) { + if (vertices.length < 3) return []; + var len = vertices.length, plen = len - 1, points = [], stack = [], i, j, h = 0, x1, y1, x2, y2, u, v, a, sp; + for (i = 1; i < len; ++i) { + if (vertices[i][1] < vertices[h][1]) { + h = i; + } else if (vertices[i][1] == vertices[h][1]) { + h = vertices[i][0] < vertices[h][0] ? i : h; + } + } + for (i = 0; i < len; ++i) { + if (i === h) continue; + y1 = vertices[i][1] - vertices[h][1]; + x1 = vertices[i][0] - vertices[h][0]; + points.push({ + angle: Math.atan2(y1, x1), + index: i + }); + } + points.sort(function(a, b) { + return a.angle - b.angle; + }); + a = points[0].angle; + v = points[0].index; + u = 0; + for (i = 1; i < plen; ++i) { + j = points[i].index; + if (a == points[i].angle) { + x1 = vertices[v][0] - vertices[h][0]; + y1 = vertices[v][1] - vertices[h][1]; + x2 = vertices[j][0] - vertices[h][0]; + y2 = vertices[j][1] - vertices[h][1]; + if (x1 * x1 + y1 * y1 >= x2 * x2 + y2 * y2) { + points[i].index = -1; + } else { + points[u].index = -1; + a = points[i].angle; + u = i; + v = j; + } + } else { + a = points[i].angle; + u = i; + v = j; + } + } + stack.push(h); + for (i = 0, j = 0; i < 2; ++j) { + if (points[j].index !== -1) { + stack.push(points[j].index); + i++; + } + } + sp = stack.length; + for (;j < plen; ++j) { + if (points[j].index === -1) continue; + while (!d3_geom_hullCCW(stack[sp - 2], stack[sp - 1], points[j].index, vertices)) { + --sp; + } + stack[sp++] = points[j].index; + } + var poly = []; + for (i = 0; i < sp; ++i) { + poly.push(vertices[stack[i]]); + } + return poly; + }; + function d3_geom_hullCCW(i1, i2, i3, v) { + var t, a, b, c, d, e, f; + t = v[i1]; + a = t[0]; + b = t[1]; + t = v[i2]; + c = t[0]; + d = t[1]; + t = v[i3]; + e = t[0]; + f = t[1]; + return (f - b) * (c - a) - (d - b) * (e - a) > 0; + } + d3.geom.polygon = function(coordinates) { + coordinates.area = function() { + var i = 0, n = coordinates.length, area = coordinates[n - 1][1] * coordinates[0][0] - coordinates[n - 1][0] * coordinates[0][1]; + while (++i < n) { + area += coordinates[i - 1][1] * coordinates[i][0] - coordinates[i - 1][0] * coordinates[i][1]; + } + return area * .5; + }; + coordinates.centroid = function(k) { + var i = -1, n = coordinates.length, x = 0, y = 0, a, b = coordinates[n - 1], c; + if (!arguments.length) k = -1 / (6 * coordinates.area()); + while (++i < n) { + a = b; + b = coordinates[i]; + c = a[0] * b[1] - b[0] * a[1]; + x += (a[0] + b[0]) * c; + y += (a[1] + b[1]) * c; + } + return [ x * k, y * k ]; + }; + coordinates.clip = function(subject) { + var input, i = -1, n = coordinates.length, j, m, a = coordinates[n - 1], b, c, d; + while (++i < n) { + input = subject.slice(); + subject.length = 0; + b = coordinates[i]; + c = input[(m = input.length) - 1]; + j = -1; + while (++j < m) { + d = input[j]; + if (d3_geom_polygonInside(d, a, b)) { + if (!d3_geom_polygonInside(c, a, b)) { + subject.push(d3_geom_polygonIntersect(c, d, a, b)); + } + subject.push(d); + } else if (d3_geom_polygonInside(c, a, b)) { + subject.push(d3_geom_polygonIntersect(c, d, a, b)); + } + c = d; + } + a = b; + } + return subject; + }; + return coordinates; + }; + function d3_geom_polygonInside(p, a, b) { + return (b[0] - a[0]) * (p[1] - a[1]) < (b[1] - a[1]) * (p[0] - a[0]); + } + function d3_geom_polygonIntersect(c, d, a, b) { + var x1 = c[0], x3 = a[0], x21 = d[0] - x1, x43 = b[0] - x3, y1 = c[1], y3 = a[1], y21 = d[1] - y1, y43 = b[1] - y3, ua = (x43 * (y1 - y3) - y43 * (x1 - x3)) / (y43 * x21 - x43 * y21); + return [ x1 + ua * x21, y1 + ua * y21 ]; + } + d3.geom.voronoi = function(vertices) { + var polygons = vertices.map(function() { + return []; + }), Z = 1e6; + d3_voronoi_tessellate(vertices, function(e) { + var s1, s2, x1, x2, y1, y2; + if (e.a === 1 && e.b >= 0) { + s1 = e.ep.r; + s2 = e.ep.l; + } else { + s1 = e.ep.l; + s2 = e.ep.r; + } + if (e.a === 1) { + y1 = s1 ? s1.y : -Z; + x1 = e.c - e.b * y1; + y2 = s2 ? s2.y : Z; + x2 = e.c - e.b * y2; + } else { + x1 = s1 ? s1.x : -Z; + y1 = e.c - e.a * x1; + x2 = s2 ? s2.x : Z; + y2 = e.c - e.a * x2; + } + var v1 = [ x1, y1 ], v2 = [ x2, y2 ]; + polygons[e.region.l.index].push(v1, v2); + polygons[e.region.r.index].push(v1, v2); + }); + polygons = polygons.map(function(polygon, i) { + var cx = vertices[i][0], cy = vertices[i][1], angle = polygon.map(function(v) { + return Math.atan2(v[0] - cx, v[1] - cy); + }), order = d3.range(polygon.length).sort(function(a, b) { + return angle[a] - angle[b]; + }); + return order.filter(function(d, i) { + return !i || angle[d] - angle[order[i - 1]] > ε; + }).map(function(d) { + return polygon[d]; + }); + }); + polygons.forEach(function(polygon, i) { + var n = polygon.length; + if (!n) return polygon.push([ -Z, -Z ], [ -Z, Z ], [ Z, Z ], [ Z, -Z ]); + if (n > 2) return; + var p0 = vertices[i], p1 = polygon[0], p2 = polygon[1], x0 = p0[0], y0 = p0[1], x1 = p1[0], y1 = p1[1], x2 = p2[0], y2 = p2[1], dx = Math.abs(x2 - x1), dy = y2 - y1; + if (Math.abs(dy) < ε) { + var y = y0 < y1 ? -Z : Z; + polygon.push([ -Z, y ], [ Z, y ]); + } else if (dx < ε) { + var x = x0 < x1 ? -Z : Z; + polygon.push([ x, -Z ], [ x, Z ]); + } else { + var y = (x2 - x1) * (y1 - y0) < (x1 - x0) * (y2 - y1) ? Z : -Z, z = Math.abs(dy) - dx; + if (Math.abs(z) < ε) { + polygon.push([ dy < 0 ? y : -y, y ]); + } else { + if (z > 0) y *= -1; + polygon.push([ -Z, y ], [ Z, y ]); + } + } + }); + return polygons; + }; + var d3_voronoi_opposite = { + l: "r", + r: "l" + }; + function d3_voronoi_tessellate(vertices, callback) { + var Sites = { + list: vertices.map(function(v, i) { + return { + index: i, + x: v[0], + y: v[1] + }; + }).sort(function(a, b) { + return a.y < b.y ? -1 : a.y > b.y ? 1 : a.x < b.x ? -1 : a.x > b.x ? 1 : 0; + }), + bottomSite: null + }; + var EdgeList = { + list: [], + leftEnd: null, + rightEnd: null, + init: function() { + EdgeList.leftEnd = EdgeList.createHalfEdge(null, "l"); + EdgeList.rightEnd = EdgeList.createHalfEdge(null, "l"); + EdgeList.leftEnd.r = EdgeList.rightEnd; + EdgeList.rightEnd.l = EdgeList.leftEnd; + EdgeList.list.unshift(EdgeList.leftEnd, EdgeList.rightEnd); + }, + createHalfEdge: function(edge, side) { + return { + edge: edge, + side: side, + vertex: null, + l: null, + r: null + }; + }, + insert: function(lb, he) { + he.l = lb; + he.r = lb.r; + lb.r.l = he; + lb.r = he; + }, + leftBound: function(p) { + var he = EdgeList.leftEnd; + do { + he = he.r; + } while (he != EdgeList.rightEnd && Geom.rightOf(he, p)); + he = he.l; + return he; + }, + del: function(he) { + he.l.r = he.r; + he.r.l = he.l; + he.edge = null; + }, + right: function(he) { + return he.r; + }, + left: function(he) { + return he.l; + }, + leftRegion: function(he) { + return he.edge == null ? Sites.bottomSite : he.edge.region[he.side]; + }, + rightRegion: function(he) { + return he.edge == null ? Sites.bottomSite : he.edge.region[d3_voronoi_opposite[he.side]]; + } + }; + var Geom = { + bisect: function(s1, s2) { + var newEdge = { + region: { + l: s1, + r: s2 + }, + ep: { + l: null, + r: null + } + }; + var dx = s2.x - s1.x, dy = s2.y - s1.y, adx = dx > 0 ? dx : -dx, ady = dy > 0 ? dy : -dy; + newEdge.c = s1.x * dx + s1.y * dy + (dx * dx + dy * dy) * .5; + if (adx > ady) { + newEdge.a = 1; + newEdge.b = dy / dx; + newEdge.c /= dx; + } else { + newEdge.b = 1; + newEdge.a = dx / dy; + newEdge.c /= dy; + } + return newEdge; + }, + intersect: function(el1, el2) { + var e1 = el1.edge, e2 = el2.edge; + if (!e1 || !e2 || e1.region.r == e2.region.r) { + return null; + } + var d = e1.a * e2.b - e1.b * e2.a; + if (Math.abs(d) < 1e-10) { + return null; + } + var xint = (e1.c * e2.b - e2.c * e1.b) / d, yint = (e2.c * e1.a - e1.c * e2.a) / d, e1r = e1.region.r, e2r = e2.region.r, el, e; + if (e1r.y < e2r.y || e1r.y == e2r.y && e1r.x < e2r.x) { + el = el1; + e = e1; + } else { + el = el2; + e = e2; + } + var rightOfSite = xint >= e.region.r.x; + if (rightOfSite && el.side === "l" || !rightOfSite && el.side === "r") { + return null; + } + return { + x: xint, + y: yint + }; + }, + rightOf: function(he, p) { + var e = he.edge, topsite = e.region.r, rightOfSite = p.x > topsite.x; + if (rightOfSite && he.side === "l") { + return 1; + } + if (!rightOfSite && he.side === "r") { + return 0; + } + if (e.a === 1) { + var dyp = p.y - topsite.y, dxp = p.x - topsite.x, fast = 0, above = 0; + if (!rightOfSite && e.b < 0 || rightOfSite && e.b >= 0) { + above = fast = dyp >= e.b * dxp; + } else { + above = p.x + p.y * e.b > e.c; + if (e.b < 0) { + above = !above; + } + if (!above) { + fast = 1; + } + } + if (!fast) { + var dxs = topsite.x - e.region.l.x; + above = e.b * (dxp * dxp - dyp * dyp) < dxs * dyp * (1 + 2 * dxp / dxs + e.b * e.b); + if (e.b < 0) { + above = !above; + } + } + } else { + var yl = e.c - e.a * p.x, t1 = p.y - yl, t2 = p.x - topsite.x, t3 = yl - topsite.y; + above = t1 * t1 > t2 * t2 + t3 * t3; + } + return he.side === "l" ? above : !above; + }, + endPoint: function(edge, side, site) { + edge.ep[side] = site; + if (!edge.ep[d3_voronoi_opposite[side]]) return; + callback(edge); + }, + distance: function(s, t) { + var dx = s.x - t.x, dy = s.y - t.y; + return Math.sqrt(dx * dx + dy * dy); + } + }; + var EventQueue = { + list: [], + insert: function(he, site, offset) { + he.vertex = site; + he.ystar = site.y + offset; + for (var i = 0, list = EventQueue.list, l = list.length; i < l; i++) { + var next = list[i]; + if (he.ystar > next.ystar || he.ystar == next.ystar && site.x > next.vertex.x) { + continue; + } else { + break; + } + } + list.splice(i, 0, he); + }, + del: function(he) { + for (var i = 0, ls = EventQueue.list, l = ls.length; i < l && ls[i] != he; ++i) {} + ls.splice(i, 1); + }, + empty: function() { + return EventQueue.list.length === 0; + }, + nextEvent: function(he) { + for (var i = 0, ls = EventQueue.list, l = ls.length; i < l; ++i) { + if (ls[i] == he) return ls[i + 1]; + } + return null; + }, + min: function() { + var elem = EventQueue.list[0]; + return { + x: elem.vertex.x, + y: elem.ystar + }; + }, + extractMin: function() { + return EventQueue.list.shift(); + } + }; + EdgeList.init(); + Sites.bottomSite = Sites.list.shift(); + var newSite = Sites.list.shift(), newIntStar; + var lbnd, rbnd, llbnd, rrbnd, bisector; + var bot, top, temp, p, v; + var e, pm; + while (true) { + if (!EventQueue.empty()) { + newIntStar = EventQueue.min(); + } + if (newSite && (EventQueue.empty() || newSite.y < newIntStar.y || newSite.y == newIntStar.y && newSite.x < newIntStar.x)) { + lbnd = EdgeList.leftBound(newSite); + rbnd = EdgeList.right(lbnd); + bot = EdgeList.rightRegion(lbnd); + e = Geom.bisect(bot, newSite); + bisector = EdgeList.createHalfEdge(e, "l"); + EdgeList.insert(lbnd, bisector); + p = Geom.intersect(lbnd, bisector); + if (p) { + EventQueue.del(lbnd); + EventQueue.insert(lbnd, p, Geom.distance(p, newSite)); + } + lbnd = bisector; + bisector = EdgeList.createHalfEdge(e, "r"); + EdgeList.insert(lbnd, bisector); + p = Geom.intersect(bisector, rbnd); + if (p) { + EventQueue.insert(bisector, p, Geom.distance(p, newSite)); + } + newSite = Sites.list.shift(); + } else if (!EventQueue.empty()) { + lbnd = EventQueue.extractMin(); + llbnd = EdgeList.left(lbnd); + rbnd = EdgeList.right(lbnd); + rrbnd = EdgeList.right(rbnd); + bot = EdgeList.leftRegion(lbnd); + top = EdgeList.rightRegion(rbnd); + v = lbnd.vertex; + Geom.endPoint(lbnd.edge, lbnd.side, v); + Geom.endPoint(rbnd.edge, rbnd.side, v); + EdgeList.del(lbnd); + EventQueue.del(rbnd); + EdgeList.del(rbnd); + pm = "l"; + if (bot.y > top.y) { + temp = bot; + bot = top; + top = temp; + pm = "r"; + } + e = Geom.bisect(bot, top); + bisector = EdgeList.createHalfEdge(e, pm); + EdgeList.insert(llbnd, bisector); + Geom.endPoint(e, d3_voronoi_opposite[pm], v); + p = Geom.intersect(llbnd, bisector); + if (p) { + EventQueue.del(llbnd); + EventQueue.insert(llbnd, p, Geom.distance(p, bot)); + } + p = Geom.intersect(bisector, rrbnd); + if (p) { + EventQueue.insert(bisector, p, Geom.distance(p, bot)); + } + } else { + break; + } + } + for (lbnd = EdgeList.right(EdgeList.leftEnd); lbnd != EdgeList.rightEnd; lbnd = EdgeList.right(lbnd)) { + callback(lbnd.edge); + } + } + d3.geom.delaunay = function(vertices) { + var edges = vertices.map(function() { + return []; + }), triangles = []; + d3_voronoi_tessellate(vertices, function(e) { + edges[e.region.l.index].push(vertices[e.region.r.index]); + }); + edges.forEach(function(edge, i) { + var v = vertices[i], cx = v[0], cy = v[1]; + edge.forEach(function(v) { + v.angle = Math.atan2(v[0] - cx, v[1] - cy); + }); + edge.sort(function(a, b) { + return a.angle - b.angle; + }); + for (var j = 0, m = edge.length - 1; j < m; j++) { + triangles.push([ v, edge[j], edge[j + 1] ]); + } + }); + return triangles; + }; + d3.geom.quadtree = function(points, x1, y1, x2, y2) { + var p, i = -1, n = points.length; + if (arguments.length < 5) { + if (arguments.length === 3) { + y2 = y1; + x2 = x1; + y1 = x1 = 0; + } else { + x1 = y1 = Infinity; + x2 = y2 = -Infinity; + while (++i < n) { + p = points[i]; + if (p.x < x1) x1 = p.x; + if (p.y < y1) y1 = p.y; + if (p.x > x2) x2 = p.x; + if (p.y > y2) y2 = p.y; + } + } + } + var dx = x2 - x1, dy = y2 - y1; + if (dx > dy) y2 = y1 + dx; else x2 = x1 + dy; + function insert(n, p, x1, y1, x2, y2) { + if (isNaN(p.x) || isNaN(p.y)) return; + if (n.leaf) { + var v = n.point; + if (v) { + if (Math.abs(v.x - p.x) + Math.abs(v.y - p.y) < .01) { + insertChild(n, p, x1, y1, x2, y2); + } else { + n.point = null; + insertChild(n, v, x1, y1, x2, y2); + insertChild(n, p, x1, y1, x2, y2); + } + } else { + n.point = p; + } + } else { + insertChild(n, p, x1, y1, x2, y2); + } + } + function insertChild(n, p, x1, y1, x2, y2) { + var sx = (x1 + x2) * .5, sy = (y1 + y2) * .5, right = p.x >= sx, bottom = p.y >= sy, i = (bottom << 1) + right; + n.leaf = false; + n = n.nodes[i] || (n.nodes[i] = d3_geom_quadtreeNode()); + if (right) x1 = sx; else x2 = sx; + if (bottom) y1 = sy; else y2 = sy; + insert(n, p, x1, y1, x2, y2); + } + var root = d3_geom_quadtreeNode(); + root.add = function(p) { + insert(root, p, x1, y1, x2, y2); + }; + root.visit = function(f) { + d3_geom_quadtreeVisit(f, root, x1, y1, x2, y2); + }; + points.forEach(root.add); + return root; + }; + function d3_geom_quadtreeNode() { + return { + leaf: true, + nodes: [], + point: null + }; + } + function d3_geom_quadtreeVisit(f, node, x1, y1, x2, y2) { + if (!f(node, x1, y1, x2, y2)) { + var sx = (x1 + x2) * .5, sy = (y1 + y2) * .5, children = node.nodes; + if (children[0]) d3_geom_quadtreeVisit(f, children[0], x1, y1, sx, sy); + if (children[1]) d3_geom_quadtreeVisit(f, children[1], sx, y1, x2, sy); + if (children[2]) d3_geom_quadtreeVisit(f, children[2], x1, sy, sx, y2); + if (children[3]) d3_geom_quadtreeVisit(f, children[3], sx, sy, x2, y2); + } + } + d3.time = {}; + var d3_time = Date, d3_time_daySymbols = [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ]; + function d3_time_utc() { + this._ = new Date(arguments.length > 1 ? Date.UTC.apply(this, arguments) : arguments[0]); + } + d3_time_utc.prototype = { + getDate: function() { + return this._.getUTCDate(); + }, + getDay: function() { + return this._.getUTCDay(); + }, + getFullYear: function() { + return this._.getUTCFullYear(); + }, + getHours: function() { + return this._.getUTCHours(); + }, + getMilliseconds: function() { + return this._.getUTCMilliseconds(); + }, + getMinutes: function() { + return this._.getUTCMinutes(); + }, + getMonth: function() { + return this._.getUTCMonth(); + }, + getSeconds: function() { + return this._.getUTCSeconds(); + }, + getTime: function() { + return this._.getTime(); + }, + getTimezoneOffset: function() { + return 0; + }, + valueOf: function() { + return this._.valueOf(); + }, + setDate: function() { + d3_time_prototype.setUTCDate.apply(this._, arguments); + }, + setDay: function() { + d3_time_prototype.setUTCDay.apply(this._, arguments); + }, + setFullYear: function() { + d3_time_prototype.setUTCFullYear.apply(this._, arguments); + }, + setHours: function() { + d3_time_prototype.setUTCHours.apply(this._, arguments); + }, + setMilliseconds: function() { + d3_time_prototype.setUTCMilliseconds.apply(this._, arguments); + }, + setMinutes: function() { + d3_time_prototype.setUTCMinutes.apply(this._, arguments); + }, + setMonth: function() { + d3_time_prototype.setUTCMonth.apply(this._, arguments); + }, + setSeconds: function() { + d3_time_prototype.setUTCSeconds.apply(this._, arguments); + }, + setTime: function() { + d3_time_prototype.setTime.apply(this._, arguments); + } + }; + var d3_time_prototype = Date.prototype; + var d3_time_formatDateTime = "%a %b %e %X %Y", d3_time_formatDate = "%m/%d/%Y", d3_time_formatTime = "%H:%M:%S"; + var d3_time_days = [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ], d3_time_dayAbbreviations = [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ], d3_time_months = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ], d3_time_monthAbbreviations = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ]; + d3.time.format = function(template) { + var n = template.length; + function format(date) { + var string = [], i = -1, j = 0, c, p, f; + while (++i < n) { + if (template.charCodeAt(i) === 37) { + string.push(template.substring(j, i)); + if ((p = d3_time_formatPads[c = template.charAt(++i)]) != null) c = template.charAt(++i); + if (f = d3_time_formats[c]) c = f(date, p == null ? c === "e" ? " " : "0" : p); + string.push(c); + j = i + 1; + } + } + string.push(template.substring(j, i)); + return string.join(""); + } + format.parse = function(string) { + var d = { + y: 1900, + m: 0, + d: 1, + H: 0, + M: 0, + S: 0, + L: 0 + }, i = d3_time_parse(d, template, string, 0); + if (i != string.length) return null; + if ("p" in d) d.H = d.H % 12 + d.p * 12; + var date = new d3_time(); + date.setFullYear(d.y, d.m, d.d); + date.setHours(d.H, d.M, d.S, d.L); + return date; + }; + format.toString = function() { + return template; + }; + return format; + }; + function d3_time_parse(date, template, string, j) { + var c, p, i = 0, n = template.length, m = string.length; + while (i < n) { + if (j >= m) return -1; + c = template.charCodeAt(i++); + if (c === 37) { + p = d3_time_parsers[template.charAt(i++)]; + if (!p || (j = p(date, string, j)) < 0) return -1; + } else if (c != string.charCodeAt(j++)) { + return -1; + } + } + return j; + } + function d3_time_formatRe(names) { + return new RegExp("^(?:" + names.map(d3.requote).join("|") + ")", "i"); + } + function d3_time_formatLookup(names) { + var map = new d3_Map(), i = -1, n = names.length; + while (++i < n) map.set(names[i].toLowerCase(), i); + return map; + } + function d3_time_formatPad(value, fill, width) { + value += ""; + var length = value.length; + return length < width ? new Array(width - length + 1).join(fill) + value : value; + } + var d3_time_dayRe = d3_time_formatRe(d3_time_days), d3_time_dayAbbrevRe = d3_time_formatRe(d3_time_dayAbbreviations), d3_time_monthRe = d3_time_formatRe(d3_time_months), d3_time_monthLookup = d3_time_formatLookup(d3_time_months), d3_time_monthAbbrevRe = d3_time_formatRe(d3_time_monthAbbreviations), d3_time_monthAbbrevLookup = d3_time_formatLookup(d3_time_monthAbbreviations); + var d3_time_formatPads = { + "-": "", + _: " ", + "0": "0" + }; + var d3_time_formats = { + a: function(d) { + return d3_time_dayAbbreviations[d.getDay()]; + }, + A: function(d) { + return d3_time_days[d.getDay()]; + }, + b: function(d) { + return d3_time_monthAbbreviations[d.getMonth()]; + }, + B: function(d) { + return d3_time_months[d.getMonth()]; + }, + c: d3.time.format(d3_time_formatDateTime), + d: function(d, p) { + return d3_time_formatPad(d.getDate(), p, 2); + }, + e: function(d, p) { + return d3_time_formatPad(d.getDate(), p, 2); + }, + H: function(d, p) { + return d3_time_formatPad(d.getHours(), p, 2); + }, + I: function(d, p) { + return d3_time_formatPad(d.getHours() % 12 || 12, p, 2); + }, + j: function(d, p) { + return d3_time_formatPad(1 + d3.time.dayOfYear(d), p, 3); + }, + L: function(d, p) { + return d3_time_formatPad(d.getMilliseconds(), p, 3); + }, + m: function(d, p) { + return d3_time_formatPad(d.getMonth() + 1, p, 2); + }, + M: function(d, p) { + return d3_time_formatPad(d.getMinutes(), p, 2); + }, + p: function(d) { + return d.getHours() >= 12 ? "PM" : "AM"; + }, + S: function(d, p) { + return d3_time_formatPad(d.getSeconds(), p, 2); + }, + U: function(d, p) { + return d3_time_formatPad(d3.time.sundayOfYear(d), p, 2); + }, + w: function(d) { + return d.getDay(); + }, + W: function(d, p) { + return d3_time_formatPad(d3.time.mondayOfYear(d), p, 2); + }, + x: d3.time.format(d3_time_formatDate), + X: d3.time.format(d3_time_formatTime), + y: function(d, p) { + return d3_time_formatPad(d.getFullYear() % 100, p, 2); + }, + Y: function(d, p) { + return d3_time_formatPad(d.getFullYear() % 1e4, p, 4); + }, + Z: d3_time_zone, + "%": function() { + return "%"; + } + }; + var d3_time_parsers = { + a: d3_time_parseWeekdayAbbrev, + A: d3_time_parseWeekday, + b: d3_time_parseMonthAbbrev, + B: d3_time_parseMonth, + c: d3_time_parseLocaleFull, + d: d3_time_parseDay, + e: d3_time_parseDay, + H: d3_time_parseHour24, + I: d3_time_parseHour24, + L: d3_time_parseMilliseconds, + m: d3_time_parseMonthNumber, + M: d3_time_parseMinutes, + p: d3_time_parseAmPm, + S: d3_time_parseSeconds, + x: d3_time_parseLocaleDate, + X: d3_time_parseLocaleTime, + y: d3_time_parseYear, + Y: d3_time_parseFullYear + }; + function d3_time_parseWeekdayAbbrev(date, string, i) { + d3_time_dayAbbrevRe.lastIndex = 0; + var n = d3_time_dayAbbrevRe.exec(string.substring(i)); + return n ? i += n[0].length : -1; + } + function d3_time_parseWeekday(date, string, i) { + d3_time_dayRe.lastIndex = 0; + var n = d3_time_dayRe.exec(string.substring(i)); + return n ? i += n[0].length : -1; + } + function d3_time_parseMonthAbbrev(date, string, i) { + d3_time_monthAbbrevRe.lastIndex = 0; + var n = d3_time_monthAbbrevRe.exec(string.substring(i)); + return n ? (date.m = d3_time_monthAbbrevLookup.get(n[0].toLowerCase()), i += n[0].length) : -1; + } + function d3_time_parseMonth(date, string, i) { + d3_time_monthRe.lastIndex = 0; + var n = d3_time_monthRe.exec(string.substring(i)); + return n ? (date.m = d3_time_monthLookup.get(n[0].toLowerCase()), i += n[0].length) : -1; + } + function d3_time_parseLocaleFull(date, string, i) { + return d3_time_parse(date, d3_time_formats.c.toString(), string, i); + } + function d3_time_parseLocaleDate(date, string, i) { + return d3_time_parse(date, d3_time_formats.x.toString(), string, i); + } + function d3_time_parseLocaleTime(date, string, i) { + return d3_time_parse(date, d3_time_formats.X.toString(), string, i); + } + function d3_time_parseFullYear(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.substring(i, i + 4)); + return n ? (date.y = +n[0], i += n[0].length) : -1; + } + function d3_time_parseYear(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.substring(i, i + 2)); + return n ? (date.y = d3_time_expandYear(+n[0]), i += n[0].length) : -1; + } + function d3_time_expandYear(d) { + return d + (d > 68 ? 1900 : 2e3); + } + function d3_time_parseMonthNumber(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.substring(i, i + 2)); + return n ? (date.m = n[0] - 1, i += n[0].length) : -1; + } + function d3_time_parseDay(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.substring(i, i + 2)); + return n ? (date.d = +n[0], i += n[0].length) : -1; + } + function d3_time_parseHour24(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.substring(i, i + 2)); + return n ? (date.H = +n[0], i += n[0].length) : -1; + } + function d3_time_parseMinutes(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.substring(i, i + 2)); + return n ? (date.M = +n[0], i += n[0].length) : -1; + } + function d3_time_parseSeconds(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.substring(i, i + 2)); + return n ? (date.S = +n[0], i += n[0].length) : -1; + } + function d3_time_parseMilliseconds(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.substring(i, i + 3)); + return n ? (date.L = +n[0], i += n[0].length) : -1; + } + var d3_time_numberRe = /^\s*\d+/; + function d3_time_parseAmPm(date, string, i) { + var n = d3_time_amPmLookup.get(string.substring(i, i += 2).toLowerCase()); + return n == null ? -1 : (date.p = n, i); + } + var d3_time_amPmLookup = d3.map({ + am: 0, + pm: 1 + }); + function d3_time_zone(d) { + var z = d.getTimezoneOffset(), zs = z > 0 ? "-" : "+", zh = ~~(Math.abs(z) / 60), zm = Math.abs(z) % 60; + return zs + d3_time_formatPad(zh, "0", 2) + d3_time_formatPad(zm, "0", 2); + } + d3.time.format.utc = function(template) { + var local = d3.time.format(template); + function format(date) { + try { + d3_time = d3_time_utc; + var utc = new d3_time(); + utc._ = date; + return local(utc); + } finally { + d3_time = Date; + } + } + format.parse = function(string) { + try { + d3_time = d3_time_utc; + var date = local.parse(string); + return date && date._; + } finally { + d3_time = Date; + } + }; + format.toString = local.toString; + return format; + }; + var d3_time_formatIso = d3.time.format.utc("%Y-%m-%dT%H:%M:%S.%LZ"); + d3.time.format.iso = Date.prototype.toISOString ? d3_time_formatIsoNative : d3_time_formatIso; + function d3_time_formatIsoNative(date) { + return date.toISOString(); + } + d3_time_formatIsoNative.parse = function(string) { + var date = new Date(string); + return isNaN(date) ? null : date; + }; + d3_time_formatIsoNative.toString = d3_time_formatIso.toString; + function d3_time_interval(local, step, number) { + function round(date) { + var d0 = local(date), d1 = offset(d0, 1); + return date - d0 < d1 - date ? d0 : d1; + } + function ceil(date) { + step(date = local(new d3_time(date - 1)), 1); + return date; + } + function offset(date, k) { + step(date = new d3_time(+date), k); + return date; + } + function range(t0, t1, dt) { + var time = ceil(t0), times = []; + if (dt > 1) { + while (time < t1) { + if (!(number(time) % dt)) times.push(new Date(+time)); + step(time, 1); + } + } else { + while (time < t1) times.push(new Date(+time)), step(time, 1); + } + return times; + } + function range_utc(t0, t1, dt) { + try { + d3_time = d3_time_utc; + var utc = new d3_time_utc(); + utc._ = t0; + return range(utc, t1, dt); + } finally { + d3_time = Date; + } + } + local.floor = local; + local.round = round; + local.ceil = ceil; + local.offset = offset; + local.range = range; + var utc = local.utc = d3_time_interval_utc(local); + utc.floor = utc; + utc.round = d3_time_interval_utc(round); + utc.ceil = d3_time_interval_utc(ceil); + utc.offset = d3_time_interval_utc(offset); + utc.range = range_utc; + return local; + } + function d3_time_interval_utc(method) { + return function(date, k) { + try { + d3_time = d3_time_utc; + var utc = new d3_time_utc(); + utc._ = date; + return method(utc, k)._; + } finally { + d3_time = Date; + } + }; + } + d3.time.second = d3_time_interval(function(date) { + return new d3_time(Math.floor(date / 1e3) * 1e3); + }, function(date, offset) { + date.setTime(date.getTime() + Math.floor(offset) * 1e3); + }, function(date) { + return date.getSeconds(); + }); + d3.time.seconds = d3.time.second.range; + d3.time.seconds.utc = d3.time.second.utc.range; + d3.time.minute = d3_time_interval(function(date) { + return new d3_time(Math.floor(date / 6e4) * 6e4); + }, function(date, offset) { + date.setTime(date.getTime() + Math.floor(offset) * 6e4); + }, function(date) { + return date.getMinutes(); + }); + d3.time.minutes = d3.time.minute.range; + d3.time.minutes.utc = d3.time.minute.utc.range; + d3.time.hour = d3_time_interval(function(date) { + var timezone = date.getTimezoneOffset() / 60; + return new d3_time((Math.floor(date / 36e5 - timezone) + timezone) * 36e5); + }, function(date, offset) { + date.setTime(date.getTime() + Math.floor(offset) * 36e5); + }, function(date) { + return date.getHours(); + }); + d3.time.hours = d3.time.hour.range; + d3.time.hours.utc = d3.time.hour.utc.range; + d3.time.day = d3_time_interval(function(date) { + var day = new d3_time(1970, 0); + day.setFullYear(date.getFullYear(), date.getMonth(), date.getDate()); + return day; + }, function(date, offset) { + date.setDate(date.getDate() + offset); + }, function(date) { + return date.getDate() - 1; + }); + d3.time.days = d3.time.day.range; + d3.time.days.utc = d3.time.day.utc.range; + d3.time.dayOfYear = function(date) { + var year = d3.time.year(date); + return Math.floor((date - year - (date.getTimezoneOffset() - year.getTimezoneOffset()) * 6e4) / 864e5); + }; + d3_time_daySymbols.forEach(function(day, i) { + day = day.toLowerCase(); + i = 7 - i; + var interval = d3.time[day] = d3_time_interval(function(date) { + (date = d3.time.day(date)).setDate(date.getDate() - (date.getDay() + i) % 7); + return date; + }, function(date, offset) { + date.setDate(date.getDate() + Math.floor(offset) * 7); + }, function(date) { + var day = d3.time.year(date).getDay(); + return Math.floor((d3.time.dayOfYear(date) + (day + i) % 7) / 7) - (day !== i); + }); + d3.time[day + "s"] = interval.range; + d3.time[day + "s"].utc = interval.utc.range; + d3.time[day + "OfYear"] = function(date) { + var day = d3.time.year(date).getDay(); + return Math.floor((d3.time.dayOfYear(date) + (day + i) % 7) / 7); + }; + }); + d3.time.week = d3.time.sunday; + d3.time.weeks = d3.time.sunday.range; + d3.time.weeks.utc = d3.time.sunday.utc.range; + d3.time.weekOfYear = d3.time.sundayOfYear; + d3.time.month = d3_time_interval(function(date) { + date = d3.time.day(date); + date.setDate(1); + return date; + }, function(date, offset) { + date.setMonth(date.getMonth() + offset); + }, function(date) { + return date.getMonth(); + }); + d3.time.months = d3.time.month.range; + d3.time.months.utc = d3.time.month.utc.range; + d3.time.year = d3_time_interval(function(date) { + date = d3.time.day(date); + date.setMonth(0, 1); + return date; + }, function(date, offset) { + date.setFullYear(date.getFullYear() + offset); + }, function(date) { + return date.getFullYear(); + }); + d3.time.years = d3.time.year.range; + d3.time.years.utc = d3.time.year.utc.range; + function d3_time_scale(linear, methods, format) { + function scale(x) { + return linear(x); + } + scale.invert = function(x) { + return d3_time_scaleDate(linear.invert(x)); + }; + scale.domain = function(x) { + if (!arguments.length) return linear.domain().map(d3_time_scaleDate); + linear.domain(x); + return scale; + }; + scale.nice = function(m) { + return scale.domain(d3_scale_nice(scale.domain(), function() { + return m; + })); + }; + scale.ticks = function(m, k) { + var extent = d3_time_scaleExtent(scale.domain()); + if (typeof m !== "function") { + var span = extent[1] - extent[0], target = span / m, i = d3.bisect(d3_time_scaleSteps, target); + if (i == d3_time_scaleSteps.length) return methods.year(extent, m); + if (!i) return linear.ticks(m).map(d3_time_scaleDate); + if (Math.log(target / d3_time_scaleSteps[i - 1]) < Math.log(d3_time_scaleSteps[i] / target)) --i; + m = methods[i]; + k = m[1]; + m = m[0].range; + } + return m(extent[0], new Date(+extent[1] + 1), k); + }; + scale.tickFormat = function() { + return format; + }; + scale.copy = function() { + return d3_time_scale(linear.copy(), methods, format); + }; + return d3.rebind(scale, linear, "range", "rangeRound", "interpolate", "clamp"); + } + function d3_time_scaleExtent(domain) { + var start = domain[0], stop = domain[domain.length - 1]; + return start < stop ? [ start, stop ] : [ stop, start ]; + } + function d3_time_scaleDate(t) { + return new Date(t); + } + function d3_time_scaleFormat(formats) { + return function(date) { + var i = formats.length - 1, f = formats[i]; + while (!f[1](date)) f = formats[--i]; + return f[0](date); + }; + } + function d3_time_scaleSetYear(y) { + var d = new Date(y, 0, 1); + d.setFullYear(y); + return d; + } + function d3_time_scaleGetYear(d) { + var y = d.getFullYear(), d0 = d3_time_scaleSetYear(y), d1 = d3_time_scaleSetYear(y + 1); + return y + (d - d0) / (d1 - d0); + } + var d3_time_scaleSteps = [ 1e3, 5e3, 15e3, 3e4, 6e4, 3e5, 9e5, 18e5, 36e5, 108e5, 216e5, 432e5, 864e5, 1728e5, 6048e5, 2592e6, 7776e6, 31536e6 ]; + var d3_time_scaleLocalMethods = [ [ d3.time.second, 1 ], [ d3.time.second, 5 ], [ d3.time.second, 15 ], [ d3.time.second, 30 ], [ d3.time.minute, 1 ], [ d3.time.minute, 5 ], [ d3.time.minute, 15 ], [ d3.time.minute, 30 ], [ d3.time.hour, 1 ], [ d3.time.hour, 3 ], [ d3.time.hour, 6 ], [ d3.time.hour, 12 ], [ d3.time.day, 1 ], [ d3.time.day, 2 ], [ d3.time.week, 1 ], [ d3.time.month, 1 ], [ d3.time.month, 3 ], [ d3.time.year, 1 ] ]; + var d3_time_scaleLocalFormats = [ [ d3.time.format("%Y"), d3_true ], [ d3.time.format("%B"), function(d) { + return d.getMonth(); + } ], [ d3.time.format("%b %d"), function(d) { + return d.getDate() != 1; + } ], [ d3.time.format("%a %d"), function(d) { + return d.getDay() && d.getDate() != 1; + } ], [ d3.time.format("%I %p"), function(d) { + return d.getHours(); + } ], [ d3.time.format("%I:%M"), function(d) { + return d.getMinutes(); + } ], [ d3.time.format(":%S"), function(d) { + return d.getSeconds(); + } ], [ d3.time.format(".%L"), function(d) { + return d.getMilliseconds(); + } ] ]; + var d3_time_scaleLinear = d3.scale.linear(), d3_time_scaleLocalFormat = d3_time_scaleFormat(d3_time_scaleLocalFormats); + d3_time_scaleLocalMethods.year = function(extent, m) { + return d3_time_scaleLinear.domain(extent.map(d3_time_scaleGetYear)).ticks(m).map(d3_time_scaleSetYear); + }; + d3.time.scale = function() { + return d3_time_scale(d3.scale.linear(), d3_time_scaleLocalMethods, d3_time_scaleLocalFormat); + }; + var d3_time_scaleUTCMethods = d3_time_scaleLocalMethods.map(function(m) { + return [ m[0].utc, m[1] ]; + }); + var d3_time_scaleUTCFormats = [ [ d3.time.format.utc("%Y"), d3_true ], [ d3.time.format.utc("%B"), function(d) { + return d.getUTCMonth(); + } ], [ d3.time.format.utc("%b %d"), function(d) { + return d.getUTCDate() != 1; + } ], [ d3.time.format.utc("%a %d"), function(d) { + return d.getUTCDay() && d.getUTCDate() != 1; + } ], [ d3.time.format.utc("%I %p"), function(d) { + return d.getUTCHours(); + } ], [ d3.time.format.utc("%I:%M"), function(d) { + return d.getUTCMinutes(); + } ], [ d3.time.format.utc(":%S"), function(d) { + return d.getUTCSeconds(); + } ], [ d3.time.format.utc(".%L"), function(d) { + return d.getUTCMilliseconds(); + } ] ]; + var d3_time_scaleUTCFormat = d3_time_scaleFormat(d3_time_scaleUTCFormats); + function d3_time_scaleUTCSetYear(y) { + var d = new Date(Date.UTC(y, 0, 1)); + d.setUTCFullYear(y); + return d; + } + function d3_time_scaleUTCGetYear(d) { + var y = d.getUTCFullYear(), d0 = d3_time_scaleUTCSetYear(y), d1 = d3_time_scaleUTCSetYear(y + 1); + return y + (d - d0) / (d1 - d0); + } + d3_time_scaleUTCMethods.year = function(extent, m) { + return d3_time_scaleLinear.domain(extent.map(d3_time_scaleUTCGetYear)).ticks(m).map(d3_time_scaleUTCSetYear); + }; + d3.time.scale.utc = function() { + return d3_time_scale(d3.scale.linear(), d3_time_scaleUTCMethods, d3_time_scaleUTCFormat); + }; + return d3; +}(); \ No newline at end of file diff --git a/inst/htmljs/vendor/jquery-1.11.3.min.js b/inst/htmljs/vendor/jquery-1.11.3.min.js new file mode 100644 index 000000000..0f60b7bd0 --- /dev/null +++ b/inst/htmljs/vendor/jquery-1.11.3.min.js @@ -0,0 +1,5 @@ +/*! jQuery v1.11.3 | (c) 2005, 2015 jQuery Foundation, Inc. | jquery.org/license */ +!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l="1.11.3",m=function(a,b){return new m.fn.init(a,b)},n=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,o=/^-ms-/,p=/-([\da-z])/gi,q=function(a,b){return b.toUpperCase()};m.fn=m.prototype={jquery:l,constructor:m,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=m.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return m.each(this,a,b)},map:function(a){return this.pushStack(m.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},m.extend=m.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||m.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(m.isPlainObject(c)||(b=m.isArray(c)))?(b?(b=!1,f=a&&m.isArray(a)?a:[]):f=a&&m.isPlainObject(a)?a:{},g[d]=m.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},m.extend({expando:"jQuery"+(l+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===m.type(a)},isArray:Array.isArray||function(a){return"array"===m.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return!m.isArray(a)&&a-parseFloat(a)+1>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==m.type(a)||a.nodeType||m.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(k.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&m.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(o,"ms-").replace(p,q)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=r(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(n,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(r(Object(a))?m.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=r(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),m.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||m.guid++,e):void 0},now:function(){return+new Date},support:k}),m.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function r(a){var b="length"in a&&a.length,c=m.type(a);return"function"===c||m.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var s=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N=M.replace("w","w#"),O="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+N+"))|)"+L+"*\\]",P=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+O+")*)|.*)\\)|)",Q=new RegExp(L+"+","g"),R=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),S=new RegExp("^"+L+"*,"+L+"*"),T=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),U=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),V=new RegExp(P),W=new RegExp("^"+N+"$"),X={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+O),PSEUDO:new RegExp("^"+P),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,aa=/[+~]/,ba=/'|\\/g,ca=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),da=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ea=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(fa){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],k=b.nodeType,"string"!=typeof a||!a||1!==k&&9!==k&&11!==k)return d;if(!e&&p){if(11!==k&&(f=_.exec(a)))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return H.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName)return H.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=1!==k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(ba,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+ra(o[l]);w=aa.test(a)&&pa(b.parentNode)||b,x=o.join(",")}if(x)try{return H.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function pa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=g.documentElement,e=g.defaultView,e&&e!==e.top&&(e.addEventListener?e.addEventListener("unload",ea,!1):e.attachEvent&&e.attachEvent("onunload",ea)),p=!f(g),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(g.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(g.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!g.getElementsByName||!g.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(g.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){var b=g.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",P)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===g||a.ownerDocument===v&&t(v,a)?-1:b===g||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,h=[a],i=[b];if(!e||!f)return a===g?-1:b===g?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?la(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},g):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ca,da),a[3]=(a[3]||a[4]||a[5]||"").replace(ca,da),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ca,da).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(Q," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(ca,da),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return W.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(ca,da).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:oa(function(){return[0]}),last:oa(function(a,b){return[b-1]}),eq:oa(function(a,b,c){return[0>c?c+b:c]}),even:oa(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:oa(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:oa(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:oa(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function sa(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function ta(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ua(a,b,c){for(var d=0,e=b.length;e>d;d++)ga(a,b[d],c);return c}function va(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function wa(a,b,c,d,e,f){return d&&!d[u]&&(d=wa(d)),e&&!e[u]&&(e=wa(e,f)),ia(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ua(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:va(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=va(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=va(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function xa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=sa(function(a){return a===b},h,!0),l=sa(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[sa(ta(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return wa(i>1&&ta(m),i>1&&ra(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&xa(a.slice(i,e)),f>e&&xa(a=a.slice(e)),f>e&&ra(a))}m.push(c)}return ta(m)}function ya(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=F.call(i));s=va(s)}H.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&ga.uniqueSort(i)}return k&&(w=v,j=t),r};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=xa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,ya(e,d)),f.selector=a}return f},i=ga.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ca,da),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ca,da),aa.test(j[0].type)&&pa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&ra(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,aa.test(a)&&pa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);m.find=s,m.expr=s.selectors,m.expr[":"]=m.expr.pseudos,m.unique=s.uniqueSort,m.text=s.getText,m.isXMLDoc=s.isXML,m.contains=s.contains;var t=m.expr.match.needsContext,u=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,v=/^.[^:#\[\.,]*$/;function w(a,b,c){if(m.isFunction(b))return m.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return m.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(v.test(b))return m.filter(b,a,c);b=m.filter(b,a)}return m.grep(a,function(a){return m.inArray(a,b)>=0!==c})}m.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?m.find.matchesSelector(d,a)?[d]:[]:m.find.matches(a,m.grep(b,function(a){return 1===a.nodeType}))},m.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(m(a).filter(function(){for(b=0;e>b;b++)if(m.contains(d[b],this))return!0}));for(b=0;e>b;b++)m.find(a,d[b],c);return c=this.pushStack(e>1?m.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(w(this,a||[],!1))},not:function(a){return this.pushStack(w(this,a||[],!0))},is:function(a){return!!w(this,"string"==typeof a&&t.test(a)?m(a):a||[],!1).length}});var x,y=a.document,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=m.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||x).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof m?b[0]:b,m.merge(this,m.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:y,!0)),u.test(c[1])&&m.isPlainObject(b))for(c in b)m.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}if(d=y.getElementById(c[2]),d&&d.parentNode){if(d.id!==c[2])return x.find(a);this.length=1,this[0]=d}return this.context=y,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):m.isFunction(a)?"undefined"!=typeof x.ready?x.ready(a):a(m):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),m.makeArray(a,this))};A.prototype=m.fn,x=m(y);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};m.extend({dir:function(a,b,c){var d=[],e=a[b];while(e&&9!==e.nodeType&&(void 0===c||1!==e.nodeType||!m(e).is(c)))1===e.nodeType&&d.push(e),e=e[b];return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),m.fn.extend({has:function(a){var b,c=m(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(m.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=t.test(a)||"string"!=typeof a?m(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&m.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?m.unique(f):f)},index:function(a){return a?"string"==typeof a?m.inArray(this[0],m(a)):m.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(m.unique(m.merge(this.get(),m(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}m.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return m.dir(a,"parentNode")},parentsUntil:function(a,b,c){return m.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return m.dir(a,"nextSibling")},prevAll:function(a){return m.dir(a,"previousSibling")},nextUntil:function(a,b,c){return m.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return m.dir(a,"previousSibling",c)},siblings:function(a){return m.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return m.sibling(a.firstChild)},contents:function(a){return m.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:m.merge([],a.childNodes)}},function(a,b){m.fn[a]=function(c,d){var e=m.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=m.filter(d,e)),this.length>1&&(C[a]||(e=m.unique(e)),B.test(a)&&(e=e.reverse())),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return m.each(a.match(E)||[],function(a,c){b[c]=!0}),b}m.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):m.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(c=a.memory&&l,d=!0,f=g||0,g=0,e=h.length,b=!0;h&&e>f;f++)if(h[f].apply(l[0],l[1])===!1&&a.stopOnFalse){c=!1;break}b=!1,h&&(i?i.length&&j(i.shift()):c?h=[]:k.disable())},k={add:function(){if(h){var d=h.length;!function f(b){m.each(b,function(b,c){var d=m.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&f(c)})}(arguments),b?e=h.length:c&&(g=d,j(c))}return this},remove:function(){return h&&m.each(arguments,function(a,c){var d;while((d=m.inArray(c,h,d))>-1)h.splice(d,1),b&&(e>=d&&e--,f>=d&&f--)}),this},has:function(a){return a?m.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],e=0,this},disable:function(){return h=i=c=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,c||k.disable(),this},locked:function(){return!i},fireWith:function(a,c){return!h||d&&!i||(c=c||[],c=[a,c.slice?c.slice():c],b?i.push(c):j(c)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!d}};return k},m.extend({Deferred:function(a){var b=[["resolve","done",m.Callbacks("once memory"),"resolved"],["reject","fail",m.Callbacks("once memory"),"rejected"],["notify","progress",m.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return m.Deferred(function(c){m.each(b,function(b,f){var g=m.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&m.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?m.extend(a,d):d}},e={};return d.pipe=d.then,m.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&m.isFunction(a.promise)?e:0,g=1===f?a:m.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&m.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;m.fn.ready=function(a){return m.ready.promise().done(a),this},m.extend({isReady:!1,readyWait:1,holdReady:function(a){a?m.readyWait++:m.ready(!0)},ready:function(a){if(a===!0?!--m.readyWait:!m.isReady){if(!y.body)return setTimeout(m.ready);m.isReady=!0,a!==!0&&--m.readyWait>0||(H.resolveWith(y,[m]),m.fn.triggerHandler&&(m(y).triggerHandler("ready"),m(y).off("ready")))}}});function I(){y.addEventListener?(y.removeEventListener("DOMContentLoaded",J,!1),a.removeEventListener("load",J,!1)):(y.detachEvent("onreadystatechange",J),a.detachEvent("onload",J))}function J(){(y.addEventListener||"load"===event.type||"complete"===y.readyState)&&(I(),m.ready())}m.ready.promise=function(b){if(!H)if(H=m.Deferred(),"complete"===y.readyState)setTimeout(m.ready);else if(y.addEventListener)y.addEventListener("DOMContentLoaded",J,!1),a.addEventListener("load",J,!1);else{y.attachEvent("onreadystatechange",J),a.attachEvent("onload",J);var c=!1;try{c=null==a.frameElement&&y.documentElement}catch(d){}c&&c.doScroll&&!function e(){if(!m.isReady){try{c.doScroll("left")}catch(a){return setTimeout(e,50)}I(),m.ready()}}()}return H.promise(b)};var K="undefined",L;for(L in m(k))break;k.ownLast="0"!==L,k.inlineBlockNeedsLayout=!1,m(function(){var a,b,c,d;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",k.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(d))}),function(){var a=y.createElement("div");if(null==k.deleteExpando){k.deleteExpando=!0;try{delete a.test}catch(b){k.deleteExpando=!1}}a=null}(),m.acceptData=function(a){var b=m.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b};var M=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,N=/([A-Z])/g;function O(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(N,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:M.test(c)?m.parseJSON(c):c}catch(e){}m.data(a,b,c)}else c=void 0}return c}function P(a){var b;for(b in a)if(("data"!==b||!m.isEmptyObject(a[b]))&&"toJSON"!==b)return!1; + +return!0}function Q(a,b,d,e){if(m.acceptData(a)){var f,g,h=m.expando,i=a.nodeType,j=i?m.cache:a,k=i?a[h]:a[h]&&h;if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||m.guid++:h),j[k]||(j[k]=i?{}:{toJSON:m.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=m.extend(j[k],b):j[k].data=m.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[m.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[m.camelCase(b)])):f=g,f}}function R(a,b,c){if(m.acceptData(a)){var d,e,f=a.nodeType,g=f?m.cache:a,h=f?a[m.expando]:m.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){m.isArray(b)?b=b.concat(m.map(b,m.camelCase)):b in d?b=[b]:(b=m.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!P(d):!m.isEmptyObject(d))return}(c||(delete g[h].data,P(g[h])))&&(f?m.cleanData([a],!0):k.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}m.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?m.cache[a[m.expando]]:a[m.expando],!!a&&!P(a)},data:function(a,b,c){return Q(a,b,c)},removeData:function(a,b){return R(a,b)},_data:function(a,b,c){return Q(a,b,c,!0)},_removeData:function(a,b){return R(a,b,!0)}}),m.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=m.data(f),1===f.nodeType&&!m._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=m.camelCase(d.slice(5)),O(f,d,e[d])));m._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){m.data(this,a)}):arguments.length>1?this.each(function(){m.data(this,a,b)}):f?O(f,a,m.data(f,a)):void 0},removeData:function(a){return this.each(function(){m.removeData(this,a)})}}),m.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=m._data(a,b),c&&(!d||m.isArray(c)?d=m._data(a,b,m.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=m.queue(a,b),d=c.length,e=c.shift(),f=m._queueHooks(a,b),g=function(){m.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return m._data(a,c)||m._data(a,c,{empty:m.Callbacks("once memory").add(function(){m._removeData(a,b+"queue"),m._removeData(a,c)})})}}),m.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthh;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},W=/^(?:checkbox|radio)$/i;!function(){var a=y.createElement("input"),b=y.createElement("div"),c=y.createDocumentFragment();if(b.innerHTML="
a",k.leadingWhitespace=3===b.firstChild.nodeType,k.tbody=!b.getElementsByTagName("tbody").length,k.htmlSerialize=!!b.getElementsByTagName("link").length,k.html5Clone="<:nav>"!==y.createElement("nav").cloneNode(!0).outerHTML,a.type="checkbox",a.checked=!0,c.appendChild(a),k.appendChecked=a.checked,b.innerHTML="",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,c.appendChild(b),b.innerHTML="",k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,k.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){k.noCloneEvent=!1}),b.cloneNode(!0).click()),null==k.deleteExpando){k.deleteExpando=!0;try{delete b.test}catch(d){k.deleteExpando=!1}}}(),function(){var b,c,d=y.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(k[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),k[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var X=/^(?:input|select|textarea)$/i,Y=/^key/,Z=/^(?:mouse|pointer|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=/^([^.]*)(?:\.(.+)|)$/;function aa(){return!0}function ba(){return!1}function ca(){try{return y.activeElement}catch(a){}}m.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=m.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return typeof m===K||a&&m.event.triggered===a.type?void 0:m.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(E)||[""],h=b.length;while(h--)f=_.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=m.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=m.event.special[o]||{},l=m.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&m.expr.match.needsContext.test(e),namespace:p.join(".")},i),(n=g[o])||(n=g[o]=[],n.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?n.splice(n.delegateCount++,0,l):n.push(l),m.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m.hasData(a)&&m._data(a);if(r&&(k=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=_.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=m.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,n=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=n.length;while(f--)g=n[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(n.splice(f,1),g.selector&&n.delegateCount--,l.remove&&l.remove.call(a,g));i&&!n.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||m.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)m.event.remove(a,o+b[j],c,d,!0);m.isEmptyObject(k)&&(delete r.handle,m._removeData(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,l,n,o=[d||y],p=j.call(b,"type")?b.type:b,q=j.call(b,"namespace")?b.namespace.split("."):[];if(h=l=d=d||y,3!==d.nodeType&&8!==d.nodeType&&!$.test(p+m.event.triggered)&&(p.indexOf(".")>=0&&(q=p.split("."),p=q.shift(),q.sort()),g=p.indexOf(":")<0&&"on"+p,b=b[m.expando]?b:new m.Event(p,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=q.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:m.makeArray(c,[b]),k=m.event.special[p]||{},e||!k.trigger||k.trigger.apply(d,c)!==!1)){if(!e&&!k.noBubble&&!m.isWindow(d)){for(i=k.delegateType||p,$.test(i+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),l=h;l===(d.ownerDocument||y)&&o.push(l.defaultView||l.parentWindow||a)}n=0;while((h=o[n++])&&!b.isPropagationStopped())b.type=n>1?i:k.bindType||p,f=(m._data(h,"events")||{})[b.type]&&m._data(h,"handle"),f&&f.apply(h,c),f=g&&h[g],f&&f.apply&&m.acceptData(h)&&(b.result=f.apply(h,c),b.result===!1&&b.preventDefault());if(b.type=p,!e&&!b.isDefaultPrevented()&&(!k._default||k._default.apply(o.pop(),c)===!1)&&m.acceptData(d)&&g&&d[p]&&!m.isWindow(d)){l=d[g],l&&(d[g]=null),m.event.triggered=p;try{d[p]()}catch(r){}m.event.triggered=void 0,l&&(d[g]=l)}return b.result}},dispatch:function(a){a=m.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(m._data(this,"events")||{})[a.type]||[],k=m.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=m.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,g=0;while((e=f.handlers[g++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(e.namespace))&&(a.handleObj=e,a.data=e.data,c=((m.event.special[e.origType]||{}).handle||e.handler).apply(f.elem,i),void 0!==c&&(a.result=c)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(e=[],f=0;h>f;f++)d=b[f],c=d.selector+" ",void 0===e[c]&&(e[c]=d.needsContext?m(c,this).index(i)>=0:m.find(c,this,null,[i]).length),e[c]&&e.push(d);e.length&&g.push({elem:i,handlers:e})}return h]","i"),ha=/^\s+/,ia=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,ja=/<([\w:]+)/,ka=/
","
"],tr:[2,"","
"],col:[2,"","
"],td:[3,"","
"],_default:k.htmlSerialize?[0,"",""]:[1,"X
","
"]},sa=da(y),ta=sa.appendChild(y.createElement("div"));ra.optgroup=ra.option,ra.tbody=ra.tfoot=ra.colgroup=ra.caption=ra.thead,ra.th=ra.td;function ua(a,b){var c,d,e=0,f=typeof a.getElementsByTagName!==K?a.getElementsByTagName(b||"*"):typeof a.querySelectorAll!==K?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||m.nodeName(d,b)?f.push(d):m.merge(f,ua(d,b));return void 0===b||b&&m.nodeName(a,b)?m.merge([a],f):f}function va(a){W.test(a.type)&&(a.defaultChecked=a.checked)}function wa(a,b){return m.nodeName(a,"table")&&m.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function xa(a){return a.type=(null!==m.find.attr(a,"type"))+"/"+a.type,a}function ya(a){var b=pa.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function za(a,b){for(var c,d=0;null!=(c=a[d]);d++)m._data(c,"globalEval",!b||m._data(b[d],"globalEval"))}function Aa(a,b){if(1===b.nodeType&&m.hasData(a)){var c,d,e,f=m._data(a),g=m._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)m.event.add(b,c,h[c][d])}g.data&&(g.data=m.extend({},g.data))}}function Ba(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!k.noCloneEvent&&b[m.expando]){e=m._data(b);for(d in e.events)m.removeEvent(b,d,e.handle);b.removeAttribute(m.expando)}"script"===c&&b.text!==a.text?(xa(b).text=a.text,ya(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),k.html5Clone&&a.innerHTML&&!m.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&W.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}}m.extend({clone:function(a,b,c){var d,e,f,g,h,i=m.contains(a.ownerDocument,a);if(k.html5Clone||m.isXMLDoc(a)||!ga.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(ta.innerHTML=a.outerHTML,ta.removeChild(f=ta.firstChild)),!(k.noCloneEvent&&k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||m.isXMLDoc(a)))for(d=ua(f),h=ua(a),g=0;null!=(e=h[g]);++g)d[g]&&Ba(e,d[g]);if(b)if(c)for(h=h||ua(a),d=d||ua(f),g=0;null!=(e=h[g]);g++)Aa(e,d[g]);else Aa(a,f);return d=ua(f,"script"),d.length>0&&za(d,!i&&ua(a,"script")),d=h=e=null,f},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,l,n=a.length,o=da(b),p=[],q=0;n>q;q++)if(f=a[q],f||0===f)if("object"===m.type(f))m.merge(p,f.nodeType?[f]:f);else if(la.test(f)){h=h||o.appendChild(b.createElement("div")),i=(ja.exec(f)||["",""])[1].toLowerCase(),l=ra[i]||ra._default,h.innerHTML=l[1]+f.replace(ia,"<$1>")+l[2],e=l[0];while(e--)h=h.lastChild;if(!k.leadingWhitespace&&ha.test(f)&&p.push(b.createTextNode(ha.exec(f)[0])),!k.tbody){f="table"!==i||ka.test(f)?""!==l[1]||ka.test(f)?0:h:h.firstChild,e=f&&f.childNodes.length;while(e--)m.nodeName(j=f.childNodes[e],"tbody")&&!j.childNodes.length&&f.removeChild(j)}m.merge(p,h.childNodes),h.textContent="";while(h.firstChild)h.removeChild(h.firstChild);h=o.lastChild}else p.push(b.createTextNode(f));h&&o.removeChild(h),k.appendChecked||m.grep(ua(p,"input"),va),q=0;while(f=p[q++])if((!d||-1===m.inArray(f,d))&&(g=m.contains(f.ownerDocument,f),h=ua(o.appendChild(f),"script"),g&&za(h),c)){e=0;while(f=h[e++])oa.test(f.type||"")&&c.push(f)}return h=null,o},cleanData:function(a,b){for(var d,e,f,g,h=0,i=m.expando,j=m.cache,l=k.deleteExpando,n=m.event.special;null!=(d=a[h]);h++)if((b||m.acceptData(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)n[e]?m.event.remove(d,e):m.removeEvent(d,e,g.handle);j[f]&&(delete j[f],l?delete d[i]:typeof d.removeAttribute!==K?d.removeAttribute(i):d[i]=null,c.push(f))}}}),m.fn.extend({text:function(a){return V(this,function(a){return void 0===a?m.text(this):this.empty().append((this[0]&&this[0].ownerDocument||y).createTextNode(a))},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wa(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wa(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?m.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||m.cleanData(ua(c)),c.parentNode&&(b&&m.contains(c.ownerDocument,c)&&za(ua(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&m.cleanData(ua(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&m.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return m.clone(this,a,b)})},html:function(a){return V(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(fa,""):void 0;if(!("string"!=typeof a||ma.test(a)||!k.htmlSerialize&&ga.test(a)||!k.leadingWhitespace&&ha.test(a)||ra[(ja.exec(a)||["",""])[1].toLowerCase()])){a=a.replace(ia,"<$1>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(m.cleanData(ua(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,m.cleanData(ua(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,n=this,o=l-1,p=a[0],q=m.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&na.test(p))return this.each(function(c){var d=n.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(i=m.buildFragment(a,this[0].ownerDocument,!1,this),c=i.firstChild,1===i.childNodes.length&&(i=c),c)){for(g=m.map(ua(i,"script"),xa),f=g.length;l>j;j++)d=i,j!==o&&(d=m.clone(d,!0,!0),f&&m.merge(g,ua(d,"script"))),b.call(this[j],d,j);if(f)for(h=g[g.length-1].ownerDocument,m.map(g,ya),j=0;f>j;j++)d=g[j],oa.test(d.type||"")&&!m._data(d,"globalEval")&&m.contains(h,d)&&(d.src?m._evalUrl&&m._evalUrl(d.src):m.globalEval((d.text||d.textContent||d.innerHTML||"").replace(qa,"")));i=c=null}return this}}),m.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){m.fn[a]=function(a){for(var c,d=0,e=[],g=m(a),h=g.length-1;h>=d;d++)c=d===h?this:this.clone(!0),m(g[d])[b](c),f.apply(e,c.get());return this.pushStack(e)}});var Ca,Da={};function Ea(b,c){var d,e=m(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:m.css(e[0],"display");return e.detach(),f}function Fa(a){var b=y,c=Da[a];return c||(c=Ea(a,b),"none"!==c&&c||(Ca=(Ca||m("