├── .gitignore ├── LICENSE ├── README.md ├── implementations ├── R │ ├── run_once.r │ ├── sample_output.pdf │ └── sunlight_R_style.R └── d3 │ ├── assets │ ├── css │ │ ├── bootstrap-theme.min.css │ │ ├── style.css │ │ └── widget.css │ ├── img │ │ └── SunlightFoundation_logo.png │ ├── js │ │ ├── bar.js │ │ ├── basic_structure.js │ │ ├── cfpb.js │ │ ├── cfpb_bar.js │ │ ├── cfpb_mortgage.js │ │ ├── cfpb_widget.js │ │ ├── hbar.js │ │ ├── lib │ │ │ └── d3.v3.min.js │ │ ├── line.js │ │ ├── pie.js │ │ └── widget.js │ └── specs │ ├── data │ ├── cfpb_lines.csv │ ├── post_debt.csv │ ├── pre_debt.csv │ ├── test_data.json │ ├── test_data_line.json │ └── test_data_pie.json │ ├── embeddable.html │ ├── embedding.html │ └── index.html └── specs ├── assets └── fonts.example.zip ├── docs └── Sunlight-StyleGuide-DataViz.pdf └── styles.json /.gitignore: -------------------------------------------------------------------------------- 1 | fonts.zip 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunlightlabs/chartoff/7ec5429d986f7cbf5bfc262967b0b2edaf5d9699/LICENSE -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | chartoff 2 | ======== 3 | 4 | repository for development of visuals in accordance with the sunlight style guide 5 | -------------------------------------------------------------------------------- /implementations/R/run_once.r: -------------------------------------------------------------------------------- 1 | install.packages("extrafont") 2 | install.packages("ggplot2") 3 | install.packages("grid") 4 | library(extrafont) 5 | font_import() 6 | loadfonts() -------------------------------------------------------------------------------- /implementations/R/sample_output.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunlightlabs/chartoff/7ec5429d986f7cbf5bfc262967b0b2edaf5d9699/implementations/R/sample_output.pdf -------------------------------------------------------------------------------- /implementations/R/sunlight_R_style.R: -------------------------------------------------------------------------------- 1 | library('ggplot2') 2 | library('grid') 3 | 4 | #resize window to 650 px width 5 | quartz.options(width = 9.0278, height = 5.7624) 6 | 7 | # Untested, but in theory the below line should work in windows (and comment out above line) 8 | #windows.options(width = 9.0278, height = 6) 9 | 10 | #################### redefine default ggplot theme ################### 11 | theme_new <- theme_set(theme_bw()) 12 | theme_new <- theme_update( 13 | #background, margins, title 14 | plot.background = element_rect(fill="#EFECEA", color= NA), 15 | plot.margin = unit(c(2.8835,.582083,2.0748,.9378), "cm"), 16 | plot.title = element_text(vjust = 7.9746, hjust = 0, size = 20, family = "ITC Franklin Gothic Std Demi Condensed"), 17 | 18 | #plot area and grid 19 | panel.border = element_blank(), 20 | panel.background = element_rect(fill="transparent", size = 0), 21 | panel.grid.minor = element_blank(), 22 | panel.grid.major = element_line(size=.4703669, color = "#FFFFFF"), 23 | 24 | #axes, ticks, and axis labels 25 | axis.line = element_line(size=.4703669, color = "#FFFFFF"), 26 | axis.ticks = element_blank(), 27 | axis.ticks.length = unit(.20888, "cm"), 28 | axis.text.x = element_text(size = 12, family = "ITC Franklin Gothic Std Demi"), 29 | axis.text.y = element_text(size = 12, family = "ITC Franklin Gothic Std Demi"), 30 | axis.title.x = element_text(vjust = -2.1804, size = 12, family = "Franklin Gothic Book"), 31 | axis.title.y = element_text(vjust = -1.2633, angle = -90, size = 12, family = "Franklin Gothic Book"), 32 | 33 | #legend 34 | legend.background = element_rect(fill = "#E5E2E0", size = .4703669, color = "#C0C0BB"), 35 | legend.text = element_text(size = 10, family = "Franklin Gothic Book"), 36 | legend.title = element_blank(), 37 | legend.key = element_rect(fill="#E5E2E0", colour= "#E5E2E0", size = 0), 38 | legend.key.height = unit(1, "cm"), 39 | legend.key.width = unit(.645,"cm"), 40 | legend.margin = unit(1.5741,"cm") 41 | ) 42 | 43 | 44 | 45 | 46 | 47 | ############################# 48 | 49 | #Redefine default discrete colors, up to 9 colors. 50 | #Change 'Set1' to 'Set2' to use D/R/I colors 51 | scale_colour_discrete <- function(...) scale_colour_sunlight(..., palette="Set1") 52 | scale_fill_discrete <- function(...) scale_fill_sunlight(... , palette="Set1") 53 | 54 | 55 | ########### Example plots ################# 56 | 57 | ####Bar 58 | ##1 color 59 | #print(ggplot(mtcars, aes(factor(cyl))) + geom_bar() + coord_cartesian(ylim = c(0, 100))+ggtitle("Title")) 60 | 61 | ##2 colors 62 | #print(qplot(factor(cyl), data=mtcars, geom="bar", fill=factor(vs)) + ggtitle("Title") + geom_bar()) 63 | 64 | ##3 colors 65 | #print(qplot(factor(cyl), data=mtcars, geom="bar", fill=factor(cyl))+ggtitle("Title")) 66 | 67 | ##5 colors 68 | #print(ggplot(diamonds, aes(clarity, fill=cut)) + geom_bar() +ggtitle("Title") + coord_cartesian(ylim = c(0, 15000))) 69 | 70 | 71 | 72 | ####Scatter 73 | ##3 colors 74 | #print(ggplot(mtcars, aes(wt, mpg))+geom_point(aes(colour = factor(cyl)))+ggtitle("Title")) 75 | 76 | ##9 colors 77 | # dsamp <- diamonds[sample(nrow(diamonds), 1000), ] 78 | # d <- qplot(carat, price, data=dsamp, colour=clarity, size = 3) 79 | # print(d+ggtitle("Title")) 80 | 81 | ####Line 82 | # mry <- do.call(rbind, by(movies, round(movies$rating), function(df) { 83 | # nums <- tapply(df$length, df$year, length) 84 | # data.frame(rating=round(df$rating[1]), year = as.numeric(names(nums)), number=as.vector(nums)) 85 | # })) 86 | # print(ggplot(mry, aes(x=year, y=number, group=rating))+ geom_line(aes(colour=rating))+ggtitle("Title")) 87 | 88 | 89 | 90 | #################### Functions to Define sunlight colors ##################### 91 | divlist<-c("BrBG","PiYG","PRGn","PuOr","RdBu","RdGy","RdYlBu","RdYlGn","Spectral") 92 | quallist<-c("Accent","Dark2","Paired","Pastel1","Pastel2","Set1","Set2","Set3") 93 | seqlist<-c("Blues","BuGn","BuPu","GnBu","Greens","Greys","Oranges","OrRd", 94 | "PuBu","PuBuGn","PuRd","Purples","RdPu","Reds","YlGn","YlGnBu","YlOrBr","YlOrRd") 95 | 96 | divnum <- rep(11, length(divlist)) 97 | qualnum <- c( 8, 8, 12, 9, 8, 9, 8, 12) 98 | seqnum <- rep(9, length(seqlist)) 99 | 100 | namelist<-c(divlist,quallist,seqlist) 101 | maxcolors <- c(divnum,qualnum,seqnum) 102 | catlist<-rep(c("div","qual","seq"),c(length(divlist),length(quallist),length(seqlist))) 103 | 104 | sunlight.pal.info<-data.frame(maxcolors=maxcolors,category=catlist,row.names=namelist) 105 | 106 | 107 | sunlight.pal<-function(n,name){ 108 | if(!(name %in% namelist)){ 109 | stop(paste(name,"is not a valid palette name for sunlight.pal\n")) 110 | } 111 | if(n<3){ 112 | warning("minimal value for n is 3, returning requested palette with 3 different levels\n") 113 | return(sunlight.pal(3,name)) 114 | } 115 | if(n>maxcolors[which(name==namelist)]){ 116 | warning(paste("n too large, allowed maximum for palette",name,"is",maxcolors[which(name==namelist)]), 117 | "\nReturning the palette you asked for with that many colors\n") 118 | return(sunlight.pal(maxcolors[which(name==namelist)],name)) 119 | } 120 | 121 | switch(name, 122 | 123 | Set1 = switch(n, 124 | 125 | rgb(c(227), 126 | c(186), 127 | c(34),maxColorValue=255), 128 | rgb(c(227,230), 129 | c(186,132), 130 | c(34,42),maxColorValue=255), 131 | rgb(c(227,230,0), 132 | c(186,132,93), 133 | c(34,42,110),maxColorValue=255), 134 | rgb(c(227,230,0,104), 135 | c(186,132,93,70), 136 | c(34,42,110,100),maxColorValue=255), 137 | rgb(c(227,230,0,104,15), 138 | c(186,132,93,70,140), 139 | c(34,42,110,100,121),maxColorValue=255), 140 | rgb(c(227,230,0,104,15,189), 141 | c(186,132,93,70,140,45), 142 | c(34,42,110,100,121,40),maxColorValue=255), 143 | rgb(c(227,230,0,104,15,189,92), 144 | c(186,132,93,70,140,45,129), 145 | c(34,42,110,100,121,40,0),maxColorValue=255), 146 | rgb(c(227,230,0,104,15,189,92,151), 147 | c(186,132,93,70,140,45,129,143), 148 | c(34,42,110,100,121,40,0,128),maxColorValue=255) 149 | ), 150 | Set2 = switch(n, 151 | rgb(c(154), 152 | c(62), 153 | c(37),maxColorValue=255), 154 | rgb(c(154,21), 155 | c(62,107), 156 | c(37,144),maxColorValue=255), 157 | rgb(c(154,21,112), 158 | c(62,107,130), 159 | c(37,144,89),maxColorValue=255) 160 | ) 161 | 162 | ) 163 | } 164 | 165 | display.sunlight.pal<-function(n,name){ 166 | if(!(name %in% namelist)){ 167 | stop(paste(name,"is not a valid palette name for sunlight.pal\n")) 168 | } 169 | if(n<3){ 170 | warning("minimal value for n is 3, displaying requested palette with 3 different levels\n") 171 | return(display.sunlight.pal(3,name)) 172 | } 173 | if(n>maxcolors[which(name==namelist)]){ 174 | warning(paste("n too large, allowed maximum for palette",name,"is",maxcolors[which(name==namelist)]), 175 | "\nDisplaying the palette you asked for with that many colors\n") 176 | return(display.sunlight.pal(maxcolors[which(name==namelist)],name)) 177 | } 178 | 179 | 180 | if(length(which(name==quallist))>0) palattr<-"(qualitative)" 181 | if(length(which(name==divlist))>0) palattr<-"(divergent)" 182 | if(length(which(name==seqlist))>0) palattr<-"(sequential)" 183 | image(1:n,1,as.matrix(1:n),col=sunlight.pal(n,name), 184 | xlab=paste(name,palattr),ylab="",xaxt="n",yaxt="n",bty="n") 185 | 186 | } 187 | 188 | display.sunlight.all <- 189 | function (n=NULL, type="all", select=NULL, exact.n=TRUE) 190 | { 191 | gaplist <- "" 192 | 193 | totallist <- c(divlist, gaplist, quallist,gaplist, seqlist) 194 | gapnum <- max(c(divnum,qualnum,seqnum)) 195 | totnum <- c(divnum, gapnum, qualnum, gapnum, seqnum) 196 | if (!(type %in% c("div","qual","seq","all"))) { 197 | stop(paste(type, "is not a valid name for a color list\n")) 198 | } 199 | colorlist <- switch(type, div=divlist, 200 | qual=quallist, seq=seqlist, 201 | all=totallist) 202 | maxnum <- switch(type, div=divnum, 203 | qual=qualnum, 204 | seq=seqnum, 205 | all=totnum) 206 | if(!is.null(select)){colorlist <- colorlist[select] 207 | maxnum <- maxnum[select] 208 | if(any(is.na(colorlist))) 209 | stop(paste("Illegal value(s) of select: ", 210 | paste(select[is.na(colorlist)], 211 | collapse=" "))) 212 | } 213 | palattr <- switch(type, qual="qualitative", div 214 | ="divergent", seq="sequential", 215 | all="qualitative+divergent+sequential") 216 | if(is.null(n))n <- maxnum 217 | if(length(n)==1)n <- rep(n, length(colorlist)) 218 | if(exact.n){ 219 | keep <- n<=maxnum 220 | colorlist <- colorlist[keep] 221 | n <- n[keep] 222 | maxnum <- maxnum[keep] 223 | } 224 | if (any(n < 3) | exact.n & any(n>maxnum)| 225 | length(n)!=length(colorlist)){ 226 | warning("Illegal vector of color numbers") 227 | print(paste(n, collapse=" ")) 228 | } 229 | n[n<3] <- 3 230 | n[n>maxnum] <- maxnum[n>maxnum] 231 | nr <- length(colorlist) 232 | nc <- max(n) 233 | ylim <- c(0,nr) 234 | oldpar <- par(mgp=c(2,0.25,0)) 235 | on.exit(par(oldpar)) 236 | plot(1,1,xlim=c(0,nc),ylim=ylim,type="n", axes=FALSE, bty="n", 237 | xlab="",ylab="") 238 | for(i in 1:nr) 239 | {nj <- n[i] 240 | if(colorlist[i]=="")next 241 | shadi <- sunlight.pal(nj, colorlist[i]) 242 | rect(xleft=0:(nj-1), ybottom=i-1, xright=1:nj, ytop=i-0.2, col=shadi, 243 | border="light grey") 244 | } 245 | text(rep(-0.1,nr),(1:nr)-0.6, labels=colorlist, xpd=TRUE, adj=1) 246 | } 247 | 248 | 249 | pal_name <- function(palette, type) { 250 | if (is.character(palette)) { 251 | if (!palette %in% RColorBrewer:::namelist) { 252 | warning("Unknown palette ", palette) 253 | palette <- "Set1" 254 | } 255 | return(palette) 256 | } 257 | 258 | switch(type, 259 | div = divlist, 260 | qual = quallist, 261 | seq = seqlist, 262 | stop("Unknown palette type. Should be 'div', 'qual' or 'seq'", 263 | call. = FALSE) 264 | )[palette] 265 | } 266 | 267 | sunlight_pal <- function(type = "seq", palette = 1) { 268 | pal <- pal_name(palette, type) 269 | 270 | function(n) { 271 | if (n < 3) 272 | suppressWarnings(sunlight.pal(n, pal))[seq_len(n)] 273 | else 274 | sunlight.pal(n, pal)[seq_len(n)] 275 | } 276 | } 277 | 278 | scale_colour_sunlight <- function(..., type = "seq", palette = 1) { 279 | discrete_scale("colour", "sunlight", sunlight_pal(type, palette), ...) 280 | } 281 | 282 | #' @export 283 | #' @rdname scale_sunlight 284 | scale_fill_sunlight <- function(..., type = "seq", palette = 1) { 285 | discrete_scale("fill", "sunlight", sunlight_pal(type, palette), ...) 286 | } -------------------------------------------------------------------------------- /implementations/d3/assets/css/bootstrap-theme.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.0.3 (http://getbootstrap.com) 3 | * Copyright 2013 Twitter, Inc. 4 | * Licensed under http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | 7 | .btn-default,.btn-primary,.btn-success,.btn-info,.btn-warning,.btn-danger{text-shadow:0 -1px 0 rgba(0,0,0,0.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075)}.btn-default:active,.btn-primary:active,.btn-success:active,.btn-info:active,.btn-warning:active,.btn-danger:active,.btn-default.active,.btn-primary.active,.btn-success.active,.btn-info.active,.btn-warning.active,.btn-danger.active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn:active,.btn.active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffe0e0e0',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-default:hover,.btn-default:focus{background-color:#e0e0e0;background-position:0 -15px}.btn-default:active,.btn-default.active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-primary{background-image:-webkit-linear-gradient(top,#428bca 0,#2d6ca2 100%);background-image:linear-gradient(to bottom,#428bca 0,#2d6ca2 100%);background-repeat:repeat-x;border-color:#2b669a;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff2d6ca2',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-primary:hover,.btn-primary:focus{background-color:#2d6ca2;background-position:0 -15px}.btn-primary:active,.btn-primary.active{background-color:#2d6ca2;border-color:#2b669a}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);background-repeat:repeat-x;border-color:#3e8f3e;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c',endColorstr='#ff419641',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-success:hover,.btn-success:focus{background-color:#419641;background-position:0 -15px}.btn-success:active,.btn-success.active{background-color:#419641;border-color:#3e8f3e}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);background-repeat:repeat-x;border-color:#e38d13;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e',endColorstr='#ffeb9316',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-warning:hover,.btn-warning:focus{background-color:#eb9316;background-position:0 -15px}.btn-warning:active,.btn-warning.active{background-color:#eb9316;border-color:#e38d13}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);background-repeat:repeat-x;border-color:#b92c28;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f',endColorstr='#ffc12e2a',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-danger:hover,.btn-danger:focus{background-color:#c12e2a;background-position:0 -15px}.btn-danger:active,.btn-danger.active{background-color:#c12e2a;border-color:#b92c28}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);background-repeat:repeat-x;border-color:#28a4c9;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff2aabd2',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-info:hover,.btn-info:focus{background-color:#2aabd2;background-position:0 -15px}.btn-info:active,.btn-info.active{background-color:#2aabd2;border-color:#28a4c9}.thumbnail,.img-thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.075);box-shadow:0 1px 2px rgba(0,0,0,0.075)}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#ffe8e8e8',GradientType=0)}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{background-color:#357ebd;background-image:-webkit-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff357ebd',GradientType=0)}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);background-repeat:repeat-x;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#fff8f8f8',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 5px rgba(0,0,0,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 5px rgba(0,0,0,0.075)}.navbar-default .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f3f3f3 100%);background-image:linear-gradient(to bottom,#ebebeb 0,#f3f3f3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb',endColorstr='#fff3f3f3',GradientType=0);-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,0.075);box-shadow:inset 0 3px 9px rgba(0,0,0,0.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,0.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c',endColorstr='#ff222222',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.navbar-inverse .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top,#222 0,#282828 100%);background-image:linear-gradient(to bottom,#222 0,#282828 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222',endColorstr='#ff282828',GradientType=0);-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,0.25);box-shadow:inset 0 3px 9px rgba(0,0,0,0.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.navbar-static-top,.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}.alert{text-shadow:0 1px 0 rgba(255,255,255,0.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.25),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.25),0 1px 2px rgba(0,0,0,0.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);background-repeat:repeat-x;border-color:#b2dba1;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8',endColorstr='#ffc8e5bc',GradientType=0)}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);background-repeat:repeat-x;border-color:#9acfea;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7',endColorstr='#ffb9def0',GradientType=0)}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);background-repeat:repeat-x;border-color:#f5e79e;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3',endColorstr='#fff8efc0',GradientType=0)}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);background-repeat:repeat-x;border-color:#dca7a7;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede',endColorstr='#ffe7c3c3',GradientType=0)}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb',endColorstr='#fff5f5f5',GradientType=0)}.progress-bar{background-image:-webkit-linear-gradient(top,#428bca 0,#3071a9 100%);background-image:linear-gradient(to bottom,#428bca 0,#3071a9 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff3071a9',GradientType=0)}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c',endColorstr='#ff449d44',GradientType=0)}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff31b0d5',GradientType=0)}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e',endColorstr='#ffec971f',GradientType=0)}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f',endColorstr='#ffc9302c',GradientType=0)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.075);box-shadow:0 1px 2px rgba(0,0,0,0.075)}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{text-shadow:0 -1px 0 #3071a9;background-image:-webkit-linear-gradient(top,#428bca 0,#3278b3 100%);background-image:linear-gradient(to bottom,#428bca 0,#3278b3 100%);background-repeat:repeat-x;border-color:#3278b3;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff3278b3',GradientType=0)}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#ffe8e8e8',GradientType=0)}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff357ebd',GradientType=0)}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8',endColorstr='#ffd0e9c6',GradientType=0)}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7',endColorstr='#ffc4e3f3',GradientType=0)}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3',endColorstr='#fffaf2cc',GradientType=0)}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede',endColorstr='#ffebcccc',GradientType=0)}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);background-repeat:repeat-x;border-color:#dcdcdc;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8',endColorstr='#fff5f5f5',GradientType=0);-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,0.05),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 3px rgba(0,0,0,0.05),0 1px 0 rgba(255,255,255,0.1)} -------------------------------------------------------------------------------- /implementations/d3/assets/css/style.css: -------------------------------------------------------------------------------- 1 | /* BASIC STRUCTURE */ 2 | 3 | .graphic-container-blog { 4 | width: 650px; 5 | padding-top: 12px; 6 | background-color: #EFECEA; 7 | } 8 | 9 | .graphic-container-feature { 10 | width: 900px; 11 | padding-top: 12px; 12 | background-color: #EFECEA; 13 | } 14 | 15 | .header-container { 16 | padding-top: 12px; 17 | padding-right: 22px; 18 | padding-bottom: 8px; 19 | padding-left: 22px; 20 | background-color: #F5F3F2; 21 | overflow:auto; 22 | } 23 | 24 | .header-title { 25 | width: 70%; 26 | margin: 0; 27 | float:left; 28 | text-align: left, 29 | } 30 | 31 | .header-logo { 32 | width: 100px; 33 | height: 37.5px; 34 | background-image: url('../img/SunlightFoundation_logo.png'); 35 | background-size: 100% 100%; 36 | float:right; 37 | } 38 | 39 | /* 40 | .chart-area { 41 | padding-top: 22px; 42 | padding-right: 22px; 43 | padding-bottom: 22px; 44 | padding-left: 22px; 45 | } 46 | */ 47 | 48 | .divider { 49 | width: 100%; 50 | height: 1px; 51 | margin: 0; 52 | } 53 | 54 | .divider.solid { 55 | background-color: #FFFFFF; 56 | } 57 | 58 | .divider.dashed { 59 | border-top: 1px dashed #C0C0BB; 60 | margin-top: 1px; 61 | margin-bottom: 1px; 62 | } 63 | 64 | .footer-container { 65 | height: 22px; 66 | padding-top: 8px; 67 | padding-left: 22px; 68 | padding-right: 22px; 69 | background-color: #E5E2E0; 70 | } 71 | 72 | /* TEXT */ 73 | 74 | .test-text { 75 | fill: #000; 76 | stroke: #000; 77 | 78 | } 79 | 80 | .main-title { 81 | font-family : "Franklin Gothic Book", "Franklin Gothic Medium", "Franklin Gothic", "ITC Franklin Gothic", "Arial Narrow", sans-serif; 82 | font-size : 20px; 83 | font-weight: normal; 84 | font-style: normal; 85 | color: #635F5D; 86 | } 87 | 88 | .main-title em { 89 | font-family : "Franklin Gothic Demi Cond", "Franklin Gothic Medium Cond", "Franklin Gothic", "ITC Franklin Gothic", Arial, sans-serif; 90 | font-style: normal; 91 | } 92 | 93 | .explanatory { 94 | font-family: Georgia, Times, "Times New Roman", serif; 95 | font-size : 12px; 96 | font-weight: normal; 97 | font-style: italic; 98 | color: #8E8883; 99 | } 100 | 101 | .axis text { 102 | font-size : 12px; 103 | font-weight: normal; 104 | font-style: normal; 105 | fill: #635F5D; 106 | stroke: none; 107 | font-family : "Franklin Gothic Book", "Franklin Gothic Medium", "Franklin Gothic", "ITC Franklin Gothic", Arial, sans-serif; 108 | } 109 | 110 | .axis .tick .unit { 111 | font-family : "Franklin Gothic Demi Cond", "Franklin Gothic Medium Cond", "Franklin Gothic", "ITC Franklin Gothic", Arial, sans-serif; 112 | } 113 | 114 | .citation { 115 | font-family: Georgia, Times, "Times New Roman", serif; 116 | font-size : 10px; 117 | font-weight: normal; 118 | font-style: italic; 119 | color: #8E8883; 120 | } 121 | 122 | .citation em { 123 | font-weight: bold; 124 | font-style: normal; 125 | } 126 | 127 | .citation a { 128 | padding-left: 20px; 129 | } 130 | 131 | /* PLOT ELEMENTS */ 132 | 133 | .axis path, 134 | .axis line { 135 | fill: none; 136 | stroke: #FFFFFF; 137 | stroke-width: 1px; 138 | } 139 | 140 | .y.axis text { 141 | text-anchor: middle; 142 | } 143 | 144 | /* Button style */ 145 | .btn-sunlight { 146 | border: 3px solid #dad8d1; 147 | border: 3px solid rgba(218, 216, 209, 0.8); 148 | background: #cb610b; 149 | background-image: -webkit-linear-gradient(#cb610b 0%, #bc5b0a 55%, #cb610b 55%); 150 | background-image: -moz-linear-gradient(#cb610b 0%, #bc5b0a 55%, #cb610b 55%); 151 | background-image: -ms-linear-gradient(#cb610b 0%, #bc5b0a 55%, #cb610b 55%); 152 | background-image: -o-linear-gradient(#cb610b 0%, #bc5b0a 55%, #cb610b 55%); 153 | background-image: linear-gradient(#cb610b 0%, #bc5b0a 55%, #cb610b 55%); 154 | color: #eee; 155 | font-family: "rooney-web", "Georgia", "Times New Roman", "Times", serif; 156 | font-size: 120%; 157 | font-weight: 400; 158 | padding: 0.45em 1.8em; 159 | -webkit-font-smoothing: antialiased; 160 | -webkit-background-clip: padding; 161 | -moz-background-clip: padding; 162 | background-clip: padding-box; 163 | -webkit-border-radius: 3px; 164 | -moz-border-radius: 3px; 165 | -ms-border-radius: 3px; 166 | -o-border-radius: 3px; 167 | border-radius: 3px; 168 | text-shadow: 0 0 0; 169 | } 170 | 171 | .btn-sunlight:hover, .btn-sunlight:active, .btn-sunlight:focus { 172 | background: #b85301; 173 | background-image: -webkit-linear-gradient(#b85301 0%, #a54e01 55%, #b85301 55%); 174 | background-image: -moz-linear-gradient(#b85301 0%, #a54e01 55%, #b85301 55%); 175 | background-image: -ms-linear-gradient(#b85301 0%, #a54e01 55%, #b85301 55%); 176 | background-image: -o-linear-gradient(#b85301 0%, #a54e01 55%, #b85301 55%); 177 | background-image: linear-gradient(#b85301 0%, #a54e01 55%, #b85301 55%); 178 | color: #fff; 179 | -webkit-font-smoothing: antialiased; 180 | -webkit-background-clip: padding; 181 | -moz-background-clip: padding; 182 | background-clip: padding-box; 183 | outline: none; 184 | } 185 | 186 | 187 | -------------------------------------------------------------------------------- /implementations/d3/assets/css/widget.css: -------------------------------------------------------------------------------- 1 | body.widget { 2 | background: none; 3 | background-color: #EFECEA; 4 | margin: 0; 5 | color: #635f5d; 6 | } 7 | 8 | body.widget .header-group { 9 | position: absolute; 10 | top: 12px; 11 | left: 0; 12 | width: 100%; 13 | } 14 | body.widget .header-container { 15 | height: 40px; 16 | } 17 | 18 | body.widget .footer-group { 19 | position: absolute; 20 | bottom: 0; 21 | left: 0; 22 | width: 100%; 23 | } 24 | 25 | body.widget .header-container, body.widget .footer-container { 26 | box-sizing: content-box; 27 | line-height: normal; 28 | } 29 | 30 | body.widget .chart-area { 31 | position: absolute; 32 | left: 0; 33 | right: 0; 34 | top: 74px; 35 | bottom: 32px; 36 | } 37 | 38 | body.widget .chart-area svg { 39 | width: 100%; 40 | height: 100%; 41 | } 42 | 43 | /* things specific to this widget */ 44 | body.widget #doc-dialog .list-group { 45 | background: #aaa; 46 | overflow-y: auto; 47 | border-top-left-radius: 0; 48 | border-top-right-radius: 0; 49 | } 50 | 51 | body.widget #doc-dialog .list-group .list-group-item { 52 | border-radius: 0; 53 | } 54 | 55 | body.widget .doc-view-panel { 56 | display: none; 57 | } 58 | 59 | body.widget a.back-link { 60 | color: #333; 61 | } 62 | 63 | /* svg styles */ 64 | body.widget svg circle:hover { 65 | stroke: #ddd !important; 66 | stroke-width: 3px !important; 67 | } 68 | body.widget svg circle.selected, body.widget svg circle.selected:hover { 69 | stroke: #fff !important; 70 | stroke-width: 3px !important; 71 | } 72 | body.widget svg text { 73 | fill: #635f5d; 74 | } 75 | body.widget .meta-container table { 76 | margin-bottom: 0; 77 | border-bottom: 1px solid #ccc; 78 | background: #f4f4f4; 79 | } 80 | body.widget .meta-container table .meta-label { 81 | width: 20%; 82 | font-weight: bold; 83 | text-align: right; 84 | } 85 | body.widget .doc-view-panel .panel-body { 86 | white-space: pre-wrap; 87 | overflow-y: auto; 88 | } 89 | body.widget .doc-view-panel .panel-body::before, body.widget .doc-view-panel .panel-body::after { 90 | display: none; 91 | } 92 | body.widget .tlabel div { 93 | text-align: center; 94 | padding: 5px; 95 | background: rgba(255,255,255,0.25); 96 | line-height: 100%; 97 | font-weight: bold; 98 | color: #444240; 99 | } 100 | body.widget .annotations .title div { 101 | padding: 10px; 102 | padding-left: 20px; 103 | background: rgba(255,255,255,0.25); 104 | color: #444240; 105 | } 106 | 107 | @media (max-width: 800px), (max-height: 600px) { 108 | body.widget .zoom0 .tlabel div { 109 | width: 100px; 110 | margin-left: 50px; 111 | font-size: 80%; 112 | white-space: nowrap; 113 | overflow: hidden; 114 | text-overflow: ellipsis; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /implementations/d3/assets/img/SunlightFoundation_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunlightlabs/chartoff/7ec5429d986f7cbf5bfc262967b0b2edaf5d9699/implementations/d3/assets/img/SunlightFoundation_logo.png -------------------------------------------------------------------------------- /implementations/d3/assets/js/bar.js: -------------------------------------------------------------------------------- 1 | 2 | d3.json('assets/specs/styles.json', function(error, s) { 3 | 4 | // Parameters for top-level sizing of plot 5 | var blog_or_feature = 'blog'; 6 | var desired_height = 400; 7 | var units = 'units'; 8 | var div_selector = "#bar-chart"; 9 | var bar_color = "yellow"; 10 | 11 | // TODO: (optionally?) Apply styles dynamically 12 | 13 | d3.json('data/test_data.json', function(error, data) { 14 | 15 | var svg = d3.select(div_selector+' svg'); 16 | 17 | var maxValue = d3.max(data, function(d) { return d.value; }); 18 | 19 | var yFormatter = d3.format(",.0d"); 20 | 21 | /* 22 | * Setting margins according to longest yAxis label, default to styles.json 23 | */ 24 | 25 | // ... get default margins from specs 26 | var margin = s.plot_elements.canvas.margin; 27 | 28 | // ... create invisible text object 29 | var testText = svg.append("g") 30 | .attr("class", "axis") 31 | .append("text") 32 | .attr("class", "test-text") 33 | .attr("x", -1000) 34 | .classed("axis", "true") 35 | .text(function(d){ return yFormatter(maxValue) }); 36 | 37 | testText.append("tspan") 38 | .classed("unit", true) 39 | .text(" " + units); 40 | 41 | // ... measure width of invisible text object 42 | var yLabelWidth = Math.max(testText[0][0].getBoundingClientRect().width,0); 43 | 44 | testText.data([]).exit().remove(); 45 | 46 | // ... use larger of two margins 47 | var suggestedLeftMargin = yLabelWidth + parseInt(s.text_styles.axis_title['font-size']) + 2 + s.plot_elements.axis.title_padding; // plus 2 related to space above/below text 48 | 49 | margin.left = Math.max(margin.left, suggestedLeftMargin); 50 | 51 | // ... follow D3 margin convention as normal 52 | var width = s.plot_elements.canvas.width[blog_or_feature] - margin.left - margin.right, 53 | height = desired_height - margin.top - margin.bottom; 54 | 55 | svg.attr("width", width + margin.left + margin.right) 56 | .attr("height", height + margin.top + margin.bottom); 57 | 58 | /* 59 | * Creating scales 60 | */ 61 | 62 | var x = d3.scale.ordinal() 63 | .rangeRoundBands([0, width], .1) 64 | .domain(data.map(function(d) { return d.label; })); 65 | 66 | var y = d3.scale.linear() 67 | .range([height, 0]) 68 | .domain([0, maxValue + 2]); 69 | 70 | /* 71 | * Creating Axes and Gridlines (innerTick) 72 | */ 73 | 74 | var xAxis = d3.svg.axis() 75 | .scale(x) 76 | .innerTickSize(-height) // really long ticks become gridlines 77 | .outerTickSize(0) 78 | .tickPadding(5) 79 | .orient("bottom"); 80 | 81 | var yAxis = d3.svg.axis() 82 | .scale(y) 83 | .orient("left") 84 | .innerTickSize(-width) // really long ticks become gridlines 85 | .outerTickSize(0) 86 | .tickPadding(5) 87 | .tickValues([10, 20, 30]) // setting tick values explicitly 88 | .tickFormat(yFormatter); 89 | 90 | /* 91 | * Drawing chart 92 | */ 93 | 94 | var basicChart = svg.append("g") 95 | .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); 96 | 97 | // ... add x axis 98 | basicChart.append("g") 99 | .attr("class", "x axis") 100 | .attr("transform", "translate(0," + height + ")") 101 | .call(xAxis) 102 | .append("text") 103 | .classed("title",true) 104 | .attr("x", function() { return (width / 2.0);}) // anchors title in middle of chart 105 | .attr("y", function() { return (margin.bottom);}) 106 | .style("text-anchor", "middle") // centers title around anchor 107 | .text("X Axis Title"); 108 | 109 | // ... add y axis with value labels 110 | basicChart.append("g") 111 | .attr("class", "y axis") 112 | .call(yAxis) 113 | .append("text") 114 | .classed("title",true) 115 | .attr("transform", "rotate(-90)") 116 | .attr("x", function() { return -(height / 2.0);}) 117 | .attr("y", function() { return -(margin.left);}) 118 | .attr("dy", function() { return (parseInt(s.text_styles.axis_label['font-size'])-2); }) // minus 2 related to space above/below text 119 | .style("text-anchor", "middle") 120 | .text("Y Axis Title"); 121 | 122 | // ... add value units to value labels in separate span for distinct styling 123 | d3.select(div_selector + ' .y.axis') 124 | .selectAll('.tick') 125 | .select('text') 126 | .append("tspan") 127 | .classed("unit", true) 128 | .text(" " + units); 129 | 130 | // ... add bars using bar_color (set above) for fill 131 | basicChart.selectAll(".bar") 132 | .data(data) 133 | .enter().append("rect") 134 | .attr("class","bar") 135 | .attr("x", function(d) { return x(d.label); }) 136 | .attr("width", x.rangeBand()) 137 | .attr("y", function(d) { return y(d.value); }) 138 | .attr("height", function(d) { return height - y(d.value); }) 139 | .style("fill", function(d) { return s.colors.data.main[bar_color].hex;} ); 140 | }); 141 | 142 | }); 143 | -------------------------------------------------------------------------------- /implementations/d3/assets/js/basic_structure.js: -------------------------------------------------------------------------------- 1 | d3.json('assets/specs/styles.json', function(error, s) { 2 | 3 | // Parameters for top-level sizing of plot 4 | var blog_or_feature = 'blog'; 5 | var desired_height = 400; 6 | var units = 'units'; 7 | var div_selector = "#basic-structure"; 8 | 9 | // TODO: (optionally?) Apply styles dynamically 10 | 11 | d3.json('data/test_data.json', function(error, data) { 12 | 13 | var svg = d3.select(div_selector+' svg'); 14 | 15 | var maxValue = d3.max(data, function(d) { return d.value; }); 16 | 17 | var yFormatter = d3.format(",.0d"); 18 | 19 | /* 20 | * Setting margins according to longest yAxis label, default to styles.json 21 | */ 22 | 23 | // ... get default margins from specs 24 | var margin = s.plot_elements.canvas.margin; 25 | 26 | // ... create invisible text object 27 | var testText = svg.append("g") 28 | .attr("class", "axis") 29 | .append("text") 30 | .attr("class", "test-text") 31 | .attr("x", -1000) 32 | .classed("axis", "true") 33 | .text(function(d){ return yFormatter(maxValue) }); 34 | 35 | testText.append("tspan") 36 | .classed("unit", true) 37 | .text(" " + units); 38 | 39 | // ... measure width of invisible text object 40 | var yLabelWidth = Math.max(testText[0][0].getBoundingClientRect().width,0); 41 | 42 | testText.data([]).exit().remove(); 43 | 44 | // ... use larger of two margins 45 | var suggestedLeftMargin = yLabelWidth + parseInt(s.text_styles.axis_title['font-size']) + 2 + s.plot_elements.axis.title_padding; // plus 2 related to space above/below text 46 | 47 | margin.left = Math.max(margin.left, suggestedLeftMargin); 48 | 49 | // ... follow D3 margin convention as normal 50 | var width = s.plot_elements.canvas.width[blog_or_feature] - margin.left - margin.right, 51 | height = desired_height - margin.top - margin.bottom; 52 | 53 | svg.attr("width", width + margin.left + margin.right) 54 | .attr("height", height + margin.top + margin.bottom); 55 | 56 | /* 57 | * Creating scales 58 | */ 59 | 60 | var x = d3.scale.ordinal() 61 | .rangeRoundBands([0, width], .1) 62 | .domain(data.map(function(d) { return d.label; })); 63 | 64 | var y = d3.scale.linear() 65 | .range([height, 0]) 66 | .domain([0, maxValue + 2]); 67 | 68 | /* 69 | * Creating Axes and Gridlines (innerTick) 70 | */ 71 | 72 | var xAxis = d3.svg.axis() 73 | .scale(x) 74 | .innerTickSize(-height) // really long ticks become gridlines 75 | .outerTickSize(0) 76 | .tickPadding(5) 77 | .orient("bottom"); 78 | 79 | var yAxis = d3.svg.axis() 80 | .scale(y) 81 | .orient("left") 82 | .innerTickSize(-width) // really long ticks become gridlines 83 | .outerTickSize(0) 84 | .tickPadding(5) 85 | .tickValues([10, 20, 30]) // setting tick values explicitly 86 | .tickFormat(yFormatter); 87 | 88 | /* 89 | * Drawing chart 90 | */ 91 | 92 | var basicChart = svg.append("g") 93 | .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); 94 | 95 | // ... add x axis 96 | basicChart.append("g") 97 | .attr("class", "x axis") 98 | .attr("transform", "translate(0," + height + ")") 99 | .call(xAxis) 100 | .append("text") 101 | .classed("title",true) 102 | .attr("x", function() { return (width / 2.0);}) // anchors title in middle of chart 103 | .attr("y", function() { return (margin.bottom);}) 104 | .style("text-anchor", "middle") // centers title around anchor 105 | .text("X Axis Title"); 106 | 107 | // ... add y axis with value labels 108 | basicChart.append("g") 109 | .attr("class", "y axis") 110 | .call(yAxis) 111 | .append("text") 112 | .classed("title",true) 113 | .attr("transform", "rotate(-90)") 114 | .attr("x", function() { return -(height / 2.0);}) 115 | .attr("y", function() { return -(margin.left);}) 116 | .attr("dy", function() { return (parseInt(s.text_styles.axis_label['font-size'])-2); }) // minus 2 related to space above/below text 117 | .style("text-anchor", "middle") 118 | .text("Y Axis Title"); 119 | 120 | // ... add value units to value labels in separate span for distinct styling 121 | d3.select(div_selector + ' .y.axis') 122 | .selectAll('.tick') 123 | .select('text') 124 | .append("tspan") 125 | .classed("unit", true) 126 | .text(" " + units); 127 | 128 | }); 129 | 130 | }); 131 | -------------------------------------------------------------------------------- /implementations/d3/assets/js/cfpb.js: -------------------------------------------------------------------------------- 1 | d3.json('assets/specs/styles.json', function(error, s) { 2 | 3 | // Parameters for top-level sizing of plot 4 | var blog_or_feature = 'blog'; 5 | var desired_height = 400; 6 | var div_selector = "#cfpb-chart"; 7 | 8 | var color = d3.scale.ordinal() 9 | .range([s.colors.network_graph.yellows[2].hex, 10 | s.colors.network_graph.teals[0].hex, 11 | s.colors.network_graph.reds[1].hex, 12 | s.colors.network_graph.mints[2].hex, 13 | s.colors.network_graph.magentas[1].hex, 14 | s.colors.network_graph.blues[2].hex, 15 | s.colors.network_graph.cyans[0].hex, 16 | s.colors.network_graph.oranges[1].hex, 17 | s.colors.network_graph.pinks[2].hex, 18 | s.colors.network_graph.greens[0].hex]); 19 | 20 | // TODO: (optionally?) Apply styles dynamically 21 | 22 | d3.csv('data/cfpb_lines.csv', function(error, data) { 23 | 24 | var svg = d3.select(div_selector+' svg'); 25 | 26 | color.domain(d3.keys(data[0]).filter(function(key) { return key !== "quarter"; })); 27 | 28 | console.log(color.range()) 29 | 30 | var parseDate = d3.time.format("%Y%m%d").parse; 31 | 32 | // convert time input to d3 time objects 33 | data.forEach(function(d) { 34 | d.date = parseDate(d.quarter); 35 | }); 36 | 37 | // pivot data 38 | var products = color.domain().map(function(name) { 39 | return { 40 | name: name, 41 | values: data.map(function(d) { 42 | return {date: d.date, amount: +d[name], name:name}; 43 | }) 44 | }; 45 | }); 46 | 47 | console.log(products); 48 | 49 | var maxValue = d3.max(products, function(c) { return d3.max(c.values, function(v) { return v.amount; }); }); 50 | 51 | /* 52 | * Setting margins according to longest yAxis label, default to styles.json 53 | */ 54 | 55 | // ... get default margins from specs 56 | var margin = s.plot_elements.canvas.margin; 57 | 58 | // ... create invisible text object 59 | var testText = svg.append("g") 60 | .attr("class", "axis") 61 | .append("text") 62 | .attr("class", "test-text") 63 | .attr("x", -1000) 64 | .classed("axis", "true") 65 | .text(function(d){ return maxValue; }); 66 | 67 | // ... measure width of invisible text object 68 | var yLabelWidth = Math.max(testText[0][0].getBoundingClientRect().width,0); 69 | 70 | testText.data([]).exit().remove(); 71 | 72 | // ... use larger of two margins 73 | var suggestedLeftMargin = yLabelWidth + parseInt(s.text_styles.axis_title['font-size']) + 2 + s.plot_elements.axis.title_padding; // plus 2 related to space above/below text 74 | 75 | margin.left = Math.max(margin.left, suggestedLeftMargin); 76 | 77 | // ... follow D3 margin convention as normal 78 | var width = s.plot_elements.canvas.width[blog_or_feature] - margin.left - margin.right, 79 | height = desired_height - margin.top - margin.bottom; 80 | 81 | svg.attr("width", width + margin.left + margin.right) 82 | .attr("height", height + margin.top + margin.bottom); 83 | 84 | /* 85 | * Creating scales 86 | */ 87 | 88 | var x = d3.time.scale() 89 | .range([0, width]); 90 | 91 | var y = d3.scale.linear() 92 | .range([height, 0]); 93 | 94 | x.domain([ 95 | d3.min(data, function(d) { return d3.time.month.offset(d.date, -1);}), 96 | d3.max(data, function(d) { return d3.time.month.offset(d.date, 11);}) 97 | ]); 98 | 99 | y.domain([ 100 | // Style guide says to start at zero 101 | //d3.min(parties, function(c) { return d3.min(c.values, function(v) { return v.amount; }); }), 102 | 0, 103 | d3.max(products, function(c) { return d3.max(c.values, function(v) { return v.amount; }); }) + 100 104 | ]); 105 | 106 | /* 107 | * Creating Axes and Gridlines (innerTick) 108 | */ 109 | 110 | var xAxis = d3.svg.axis() 111 | .scale(x) 112 | .ticks(d3.time.month, 20) 113 | .innerTickSize(-height) // really long ticks become gridlines 114 | .outerTickSize(0) 115 | .tickPadding(5) 116 | .orient("bottom"); 117 | 118 | var yAxis = d3.svg.axis() 119 | .scale(y) 120 | .orient("left") 121 | .innerTickSize(-width) // really long ticks become gridlines 122 | .outerTickSize(0) 123 | .tickPadding(5) 124 | .tickFormat(function(d){ return d;}); 125 | 126 | /* 127 | * Drawing chart 128 | */ 129 | 130 | var lineChart = svg.append("g") 131 | .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); 132 | 133 | // ... add x axis 134 | lineChart.append("g") 135 | .attr("class", "x axis") 136 | .attr("transform", "translate(0," + height + ")") 137 | .call(xAxis) 138 | .append("text") 139 | .classed("title",true) 140 | .attr("x", function() { return (width / 2.0);}) // anchors title in middle of chart 141 | .attr("y", function() { return (margin.bottom);}) 142 | .style("text-anchor", "middle") // centers title around anchor 143 | .text("Date"); 144 | 145 | // ... add y axis with value labels 146 | lineChart.append("g") 147 | .attr("class", "y axis") 148 | .call(yAxis) 149 | .append("text") 150 | .classed("title",true) 151 | .attr("transform", "rotate(-90)") 152 | .attr("x", function() { return -(height / 2.0);}) 153 | .attr("y", function() { return -(margin.left);}) 154 | .attr("dy", function() { return (parseInt(s.text_styles.axis_label['font-size'])-2); }) // minus 2 related to space above/below text 155 | .style("text-anchor", "middle") 156 | .text("Number of Complaints"); 157 | 158 | // line drawing function 159 | var line = d3.svg.line() 160 | .interpolate("monotone") 161 | .x(function(d) { return x(d.date); }) 162 | .y(function(d) { return y(d.amount); }); 163 | 164 | var product = lineChart.selectAll(".product") 165 | .data(products) 166 | .enter().append("g") 167 | .attr("class", "product"); 168 | 169 | product.append("path") 170 | .attr("class", "line") 171 | .attr("d", function(d) { return line(d.values); }) 172 | .style("stroke-width", s.plot_elements.line.width) 173 | .style("fill", "none") 174 | .style("stroke", function(d) { return color(d.name); }); 175 | 176 | product.append("text") 177 | .datum(function(d) { return {name: d.name, value: d.values[d.values.length - 1]}; }) 178 | .attr("transform", function(d) { return "translate(" + x(d.value.date) + "," + y(d.value.amount) + ")"; }) 179 | .attr("x", 10) 180 | .attr("dy", ".35em") 181 | .style("font-size", s.text_styles.point_label['font-size']) 182 | .style("font-family", s.text_styles.point_label['font-family']) 183 | .text(function(d) { return d.name; }); 184 | 185 | var point = product.append("g") 186 | .attr("class", "line-point"); 187 | 188 | point.selectAll("circle") 189 | .data(function(d,i){ return d.values; }) 190 | .enter().append("circle") 191 | .attr("cx", function(d) { return x(d.date); }) 192 | .attr("cy", function(d) { return y(d.amount); }) 193 | .attr("r", 3) 194 | .style("fill", function(d) { console.log(d); return color(d.name); }) 195 | 196 | 197 | // TODO: Add Legend 198 | 199 | }); 200 | 201 | }); 202 | -------------------------------------------------------------------------------- /implementations/d3/assets/js/cfpb_bar.js: -------------------------------------------------------------------------------- 1 | 2 | d3.json('assets/specs/styles.json', function(error, s) { 3 | 4 | // Parameters for top-level sizing of plot 5 | var blog_or_feature = 'blog'; 6 | var desired_height = 400; 7 | var units = 'units'; 8 | var div_selector = "#cfpb-bar-chart"; 9 | var color = d3.scale.ordinal() 10 | .range([s.colors.network_graph.yellows[2].hex, 11 | s.colors.network_graph.teals[0].hex, 12 | s.colors.network_graph.reds[1].hex, 13 | s.colors.network_graph.mints[2].hex, 14 | s.colors.network_graph.magentas[1].hex, 15 | s.colors.network_graph.blues[2].hex, 16 | s.colors.network_graph.cyans[0].hex, 17 | s.colors.network_graph.oranges[1].hex, 18 | s.colors.network_graph.pinks[2].hex, 19 | s.colors.network_graph.greens[0].hex]); 20 | 21 | // TODO: (optionally?) Apply styles dynamically 22 | 23 | d3.csv('data/post_debt.csv', function(error, data) { 24 | 25 | var svg = d3.select(div_selector+' svg'); 26 | 27 | var maxValue = 32000; 28 | 29 | var yFormatter = d3.format(",.0d"); 30 | 31 | /* 32 | * Setting margins according to longest yAxis label, default to styles.json 33 | */ 34 | 35 | // ... get default margins from specs 36 | var margin = s.plot_elements.canvas.margin; 37 | 38 | // ... create invisible text object 39 | var testText = svg.append("g") 40 | .attr("class", "axis") 41 | .append("text") 42 | .attr("class", "test-text") 43 | .attr("x", -1000) 44 | .classed("axis", "true") 45 | .text(function(d){ return yFormatter(maxValue) }); 46 | 47 | testText.append("tspan") 48 | .classed("unit", true) 49 | .text(" " + units); 50 | 51 | // ... measure width of invisible text object 52 | var yLabelWidth = Math.max(testText[0][0].getBoundingClientRect().width,0); 53 | 54 | testText.data([]).exit().remove(); 55 | 56 | // ... use larger of two margins 57 | var suggestedLeftMargin = yLabelWidth + parseInt(s.text_styles.axis_title['font-size']) + 2 + s.plot_elements.axis.title_padding; // plus 2 related to space above/below text 58 | 59 | margin.left = Math.max(margin.left, suggestedLeftMargin); 60 | 61 | // ... follow D3 margin convention as normal 62 | var width = s.plot_elements.canvas.width[blog_or_feature] - margin.left - margin.right, 63 | height = desired_height - margin.top - margin.bottom; 64 | 65 | svg.attr("width", width + margin.left + margin.right) 66 | .attr("height", height + margin.top + margin.bottom); 67 | 68 | /* 69 | * Creating scales 70 | */ 71 | 72 | 73 | var x = d3.scale.ordinal() 74 | .rangeRoundBands([0, width], .1) 75 | .domain(data.map(function(d) { return d.Company; })); 76 | 77 | var y = d3.scale.linear() 78 | .rangeRound([height, 0]) 79 | .domain([0, maxValue + 2]); 80 | 81 | /* 82 | * Creating Axes and Gridlines (innerTick) 83 | */ 84 | 85 | var xAxis = d3.svg.axis() 86 | .scale(x) 87 | .innerTickSize(-height) // really long ticks become gridlines 88 | .outerTickSize(0) 89 | .tickPadding(5) 90 | .orient("bottom"); 91 | 92 | var yAxis = d3.svg.axis() 93 | .scale(y) 94 | .orient("left") 95 | .innerTickSize(-width) // really long ticks become gridlines 96 | .outerTickSize(0) 97 | .tickPadding(5) 98 | .tickValues([10, 20, 30]) // setting tick values explicitly 99 | .tickFormat(yFormatter); 100 | 101 | /* 102 | * Drawing chart 103 | */ 104 | 105 | color.domain(d3.keys(data[0]).filter(function(key) { return key !== "Company"; })); 106 | 107 | data.forEach(function(d) { 108 | var y0 = 0; 109 | d.products = color.domain().map(function(name) { return {name: name, y0: y0, y1: y0 += +d[name]}; }); 110 | d.total = d.products[d.products.length - 1].y1; 111 | }); 112 | 113 | svg.append("g") 114 | .attr("class", "x axis") 115 | .attr("transform", "translate(0," + height + ")") 116 | .call(xAxis) 117 | .selectAll("text") 118 | .attr("y", 0) 119 | .attr("x", 9) 120 | .attr("dy", ".35em") 121 | .attr("transform", "rotate(90)") 122 | .style("text-anchor", "start"); 123 | 124 | svg.append("g") 125 | .attr("class", "y axis") 126 | .call(yAxis) 127 | .append("text") 128 | .attr("transform", "rotate(-90)") 129 | .attr("y", 6) 130 | .attr("dy", ".71em") 131 | .style("text-anchor", "end") 132 | .text("Number of complaints"); 133 | 134 | var company = svg.selectAll(".company") 135 | .data(data) 136 | .enter().append("g") 137 | .attr("class", "g") 138 | .attr("transform", function(d) { return "translate(" + x(d.Company) + ",0)"; }); 139 | 140 | company.selectAll("rect") 141 | .data(function(d) {;return d.products; }) 142 | .enter().append("rect") 143 | .attr("width", x.rangeBand()) 144 | .attr("y", function(d) { return y(d.y1); }) 145 | .attr("height", function(d) { return y(d.y0) - y(d.y1); }) 146 | .style("fill", function(d) { return color(d.name); }); 147 | 148 | var legend = svg.selectAll(".legend") 149 | .data(color.domain().slice().reverse()) 150 | .enter().append("g") 151 | .attr("class", "legend") 152 | .attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; }); 153 | 154 | legend.append("rect") 155 | .attr("x", width - 50) 156 | .attr("width", 18) 157 | .attr("height", 18) 158 | .style("fill", color); 159 | 160 | legend.append("text") 161 | .attr("x", width - 56) 162 | .attr("y", 9) 163 | .attr("dy", ".35em") 164 | .style("text-anchor", "end") 165 | .text(function(d) { return d; }); 166 | 167 | 168 | }); 169 | 170 | }); 171 | -------------------------------------------------------------------------------- /implementations/d3/assets/js/cfpb_mortgage.js: -------------------------------------------------------------------------------- 1 | d3.json('assets/specs/styles.json', function(error, s) { 2 | 3 | // Parameters for top-level sizing of plot 4 | var blog_or_feature = 'blog'; 5 | var desired_height = 400; 6 | var div_selector = "#cfpb-mortgage-chart"; 7 | 8 | var color = d3.scale.ordinal() 9 | .range([ 10 | s.colors.network_graph.teals[0].hex, 11 | ]); 12 | 13 | // TODO: (optionally?) Apply styles dynamically 14 | 15 | d3.csv('data/cfpb_lines.csv', function(error, data) { 16 | 17 | var svg = d3.select(div_selector+' svg'); 18 | 19 | color.domain(["Mortgage"]); 20 | 21 | var parseDate = d3.time.format("%Y%m%d").parse; 22 | 23 | // convert time input to d3 time objects 24 | data.forEach(function(d) { 25 | d.date = parseDate(d.quarter); 26 | }); 27 | 28 | // pivot data 29 | var products = color.domain().map(function(name) { 30 | return { 31 | name: name, 32 | values: data.map(function(d) { 33 | return {date: d.date, amount: +d[name], name:name}; 34 | }) 35 | }; 36 | }); 37 | 38 | var maxValue = d3.max(products, function(c) { return d3.max(c.values, function(v) { return v.amount; }); }); 39 | 40 | /* 41 | * Setting margins according to longest yAxis label, default to styles.json 42 | */ 43 | 44 | // ... get default margins from specs 45 | var margin = s.plot_elements.canvas.margin; 46 | 47 | // ... create invisible text object 48 | var testText = svg.append("g") 49 | .attr("class", "axis") 50 | .append("text") 51 | .attr("class", "test-text") 52 | .attr("x", -1000) 53 | .classed("axis", "true") 54 | .text(function(d){ return maxValue; }); 55 | 56 | // ... measure width of invisible text object 57 | var yLabelWidth = Math.max(testText[0][0].getBoundingClientRect().width,0); 58 | 59 | testText.data([]).exit().remove(); 60 | 61 | // ... use larger of two margins 62 | var suggestedLeftMargin = yLabelWidth + parseInt(s.text_styles.axis_title['font-size']) + 2 + s.plot_elements.axis.title_padding; // plus 2 related to space above/below text 63 | 64 | margin.left = Math.max(margin.left, suggestedLeftMargin); 65 | 66 | // ... follow D3 margin convention as normal 67 | var width = s.plot_elements.canvas.width[blog_or_feature] - margin.left - margin.right, 68 | height = desired_height - margin.top - margin.bottom; 69 | 70 | svg.attr("width", width + margin.left + margin.right) 71 | .attr("height", height + margin.top + margin.bottom); 72 | 73 | /* 74 | * Creating scales 75 | */ 76 | 77 | var x = d3.time.scale() 78 | .range([0, width]); 79 | 80 | var y = d3.scale.linear() 81 | .range([height, 0]); 82 | 83 | x.domain([ 84 | d3.min(data, function(d) { return d3.time.month.offset(d.date, -1);}), 85 | d3.max(data, function(d) { return d3.time.month.offset(d.date, 11);}) 86 | ]); 87 | 88 | y.domain([ 89 | // Style guide says to start at zero 90 | //d3.min(parties, function(c) { return d3.min(c.values, function(v) { return v.amount; }); }), 91 | 0, 92 | d3.max(products, function(c) { return d3.max(c.values, function(v) { return v.amount; }); }) + 100 93 | ]); 94 | 95 | /* 96 | * Creating Axes and Gridlines (innerTick) 97 | */ 98 | 99 | var xAxis = d3.svg.axis() 100 | .scale(x) 101 | .ticks(5) 102 | .innerTickSize(-height) // really long ticks become gridlines 103 | .outerTickSize(0) 104 | .tickPadding(5) 105 | .orient("bottom"); 106 | 107 | var yAxis = d3.svg.axis() 108 | .scale(y) 109 | .orient("left") 110 | .innerTickSize(-width) // really long ticks become gridlines 111 | .outerTickSize(0) 112 | .tickPadding(5) 113 | .tickFormat(function(d){ return d;}); 114 | 115 | /* 116 | * Drawing chart 117 | */ 118 | 119 | var lineChart = svg.append("g") 120 | .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); 121 | 122 | // ... add x axis 123 | lineChart.append("g") 124 | .attr("class", "x axis") 125 | .attr("transform", "translate(0," + height + ")") 126 | .call(xAxis) 127 | .append("text") 128 | .classed("title",true) 129 | .attr("x", function() { return (width / 2.0);}) // anchors title in middle of chart 130 | .attr("y", function() { return (margin.bottom);}) 131 | .style("text-anchor", "middle") // centers title around anchor 132 | .text("Date"); 133 | 134 | // ... add y axis with value labels 135 | lineChart.append("g") 136 | .attr("class", "y axis") 137 | .call(yAxis) 138 | .append("text") 139 | .classed("title",true) 140 | .attr("transform", "rotate(-90)") 141 | .attr("x", function() { return -(height / 2.0);}) 142 | .attr("y", function() { return -(margin.left);}) 143 | .attr("dy", function() { return (parseInt(s.text_styles.axis_label['font-size'])-2); }) // minus 2 related to space above/below text 144 | .style("text-anchor", "middle") 145 | .text("Number of Complaints"); 146 | 147 | // line drawing function 148 | var line = d3.svg.line() 149 | .interpolate("monotone") 150 | .x(function(d) { return x(d.date); }) 151 | .y(function(d) { return y(d.amount); }); 152 | 153 | var product = lineChart.selectAll(".product") 154 | .data(products) 155 | .enter().append("g") 156 | .attr("class", "product"); 157 | 158 | product.append("path") 159 | .attr("class", "line") 160 | .attr("d", function(d) { return line(d.values); }) 161 | .style("stroke-width", s.plot_elements.line.width) 162 | .style("fill", "none") 163 | .style("stroke", function(d) { return color(d.name); }); 164 | 165 | product.append("text") 166 | .datum(function(d) { return {name: d.name, value: d.values[d.values.length - 1]}; }) 167 | .attr("transform", function(d) { return "translate(" + x(d.value.date) + "," + y(d.value.amount) + ")"; }) 168 | .attr("x", 10) 169 | .attr("dy", ".35em") 170 | .style("font-size", s.text_styles.point_label['font-size']) 171 | .style("font-family", s.text_styles.point_label['font-family']) 172 | .text(function(d) { return d.name; }); 173 | 174 | var point = product.append("g") 175 | .attr("class", "line-point"); 176 | 177 | point.selectAll("circle") 178 | .data(function(d,i){ return d.values; }) 179 | .enter().append("circle") 180 | .attr("cx", function(d) { return x(d.date); }) 181 | .attr("cy", function(d) { return y(d.amount); }) 182 | .attr("r", 3) 183 | .style("fill", function(d) { return color(d.name); }) 184 | 185 | 186 | // TODO: Add Legend 187 | 188 | }); 189 | 190 | }); 191 | -------------------------------------------------------------------------------- /implementations/d3/assets/js/cfpb_widget.js: -------------------------------------------------------------------------------- 1 | // from http://stackoverflow.com/questions/9235304/how-to-replace-the-location-hash-and-only-keep-the-last-history-entry 2 | 3 | var testvar; 4 | 5 | (function(namespace) { // Closure to protect local variable "var hash" 6 | if ('replaceState' in history) { // Yay, supported! 7 | namespace.replaceHash = function(newhash) { 8 | if ((''+newhash).charAt(0) !== '#') newhash = '#' + newhash; 9 | history.replaceState('', '', newhash); 10 | } 11 | } else { 12 | var hash = location.hash; 13 | namespace.replaceHash = function(newhash) { 14 | if (location.hash !== hash) history.back(); 15 | location.hash = newhash; 16 | }; 17 | } 18 | })(window); 19 | 20 | (function($) { 21 | // grab the query dict and see if this is a special one 22 | var queryDict = {}; 23 | location.search.substr(1).split("&").forEach(function(item) {queryDict[item.split("=")[0]] = item.split("=")[1]}); 24 | var dataset = queryDict.t ? queryDict.t : 'data'; 25 | 26 | var stylesXHR = $.getJSON('assets/specs/styles.json'); 27 | 28 | $.when(stylesXHR).done(function(stylesResult) { 29 | var styles = stylesResult; 30 | 31 | var $svg = $('svg'); 32 | 33 | /* GETTING SIZE FROM CONTEXT */ 34 | var width = $svg.width(), 35 | height = $svg.height(); 36 | 37 | var focus; 38 | 39 | /* USEFUL UTILS */ 40 | 41 | /* D3 formats */ 42 | var format = d3.format("0,000"); 43 | var percentFormat = d3.format(".4p"); 44 | 45 | /* opening the modal */ 46 | var openModalDetail = function(evt) { 47 | evt.stopPropagation(); 48 | evt.preventDefault(); 49 | 50 | var dialog = $('#doc-dialog'); 51 | dialog.modal('toggle'); 52 | 53 | window.replaceHash(focus.id); 54 | 55 | /* Add stuff to the modal dialog here */ 56 | 57 | }; 58 | 59 | var setFocus = function(d) { 60 | var focus0 = focus; 61 | focus = d; 62 | }; 63 | 64 | var draw = function(data) { 65 | var svg = d3.select($svg[0]); 66 | 67 | var all_keys = d3.keys(data[0]) 68 | .filter(function(key) { return key !== "quarter";}); 69 | 70 | var filtered_keys; 71 | 72 | if (window.location.hash != "") { 73 | filtered_keys = window.location.hash.replace('%20',' ').replace('#','').split(','); 74 | } else { 75 | filtered_keys = all_keys; 76 | }; 77 | 78 | colors = [ 79 | styles.colors.network_graph.yellows[2].hex, 80 | styles.colors.network_graph.teals[0].hex, 81 | styles.colors.network_graph.reds[1].hex, 82 | styles.colors.network_graph.mints[2].hex, 83 | styles.colors.network_graph.magentas[1].hex, 84 | styles.colors.network_graph.blues[2].hex, 85 | styles.colors.network_graph.cyans[0].hex, 86 | styles.colors.network_graph.oranges[1].hex, 87 | styles.colors.network_graph.pinks[2].hex, 88 | styles.colors.network_graph.greens[0].hex 89 | ] 90 | 91 | var color = d3.scale.ordinal() 92 | .domain(all_keys) 93 | .range(colors); 94 | 95 | var parseDate = d3.time.format("%Y%m%d").parse; 96 | 97 | data.forEach(function(d) { 98 | d.date = parseDate(d.quarter); 99 | }); 100 | 101 | //console.log(data); 102 | 103 | var products; 104 | 105 | // pivot data 106 | products = filtered_keys.map(function(name) { 107 | return { 108 | name: name, 109 | values: data.map(function(d) { 110 | return {date: d.date, amount: +d[name], name:name}; 111 | }) 112 | }; 113 | }); 114 | 115 | 116 | 117 | //console.log(products); 118 | console.log(all_keys); 119 | 120 | var maxValue = d3.max(data, function(d) { 121 | return d3.max(all_keys, function(k) { 122 | console.log(d[k]); 123 | return +d[k];}); 124 | }); 125 | 126 | console.log(maxValue); 127 | //testvar = maxValue; 128 | 129 | /* 130 | * Setting margins according to longest yAxis label, default to styles.json 131 | */ 132 | 133 | // ... get default margins from specs 134 | var margin = styles.plot_elements.canvas.margin; 135 | testvar = margin; 136 | 137 | // ... create invisible text object 138 | var testText = svg.append("g") 139 | .attr("class", "axis") 140 | .append("text") 141 | .attr("class", "test-text") 142 | .attr("x", -1000) 143 | .classed("axis", "true") 144 | .text(function(d){ return maxValue; }); 145 | 146 | // ... measure width of invisible text object 147 | var yLabelWidth = Math.max(testText[0][0].getBoundingClientRect().width,0); 148 | 149 | testText.data([]).exit().remove(); 150 | 151 | // ... use larger of two margins 152 | var suggestedLeftMargin = yLabelWidth + parseInt(styles.text_styles.axis_title['font-size']) + 2 + styles.plot_elements.axis.title_padding; // plus 2 related to space above/below text 153 | 154 | margin.left = Math.max(margin.left, suggestedLeftMargin); 155 | 156 | //svg.attr("width", width + margin.left + margin.right) 157 | // .attr("height", height + margin.top + margin.bottom); 158 | 159 | /* 160 | * Creating scales 161 | */ 162 | 163 | chart_width = width - margin.left - margin.right; 164 | chart_height = height - margin.top - margin.bottom; 165 | 166 | var x = d3.time.scale() 167 | .range([0, chart_width]); 168 | 169 | var y = d3.scale.linear() 170 | .range([chart_height, 0]); 171 | 172 | x.domain([ 173 | d3.min(data, function(d) { return d3.time.month.offset(d.date, -1);}), 174 | d3.max(data, function(d) { return d3.time.month.offset(d.date, 11);}) 175 | ]); 176 | 177 | y.domain([ 178 | // Style guide says to start at zero 179 | //d3.min(parties, function(c) { return d3.min(c.values, function(v) { return v.amount; }); }), 180 | 0, 181 | maxValue + 500 182 | ]); 183 | 184 | /* 185 | * Creating Axes and Gridlines (innerTick) 186 | */ 187 | 188 | var xAxis = d3.svg.axis() 189 | .scale(x) 190 | .ticks(d3.time.month, 20) 191 | .innerTickSize(-chart_height) // really long ticks become gridlines 192 | .outerTickSize(0) 193 | .tickPadding(5) 194 | .orient("bottom"); 195 | 196 | var yAxis = d3.svg.axis() 197 | .scale(y) 198 | .orient("left") 199 | .innerTickSize(-chart_width) // really long ticks become gridlines 200 | .outerTickSize(0) 201 | .tickPadding(5) 202 | .tickFormat(function(d){ return d;}); 203 | 204 | /* 205 | * Drawing chart 206 | */ 207 | 208 | var lineChart = svg.append("g") 209 | .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); 210 | 211 | // ... add x axis 212 | lineChart.append("g") 213 | .attr("class", "x axis") 214 | .attr("transform", "translate(0," + chart_height + ")") 215 | .call(xAxis) 216 | .append("text") 217 | .classed("title",true) 218 | .attr("x", function() { return (chart_width / 2.0);}) // anchors title in middle of chart 219 | .attr("y", function() { return (margin.bottom - 10);}) 220 | .style("text-anchor", "middle") // centers title around anchor 221 | .text("Date"); 222 | 223 | // ... add y axis with value labels 224 | lineChart.append("g") 225 | .attr("class", "y axis") 226 | .call(yAxis) 227 | .append("text") 228 | .classed("title",true) 229 | .attr("transform", "rotate(-90)") 230 | .attr("x", function() { return -(chart_height / 2.0);}) 231 | .attr("y", function() { return -(margin.left - 10);}) 232 | .attr("dy", function() { return (parseInt(styles.text_styles.axis_label['font-size'])-2); }) // minus 2 related to space above/below text 233 | .style("text-anchor", "middle") 234 | .text("Number of Complaints"); 235 | 236 | // line drawing function 237 | var line = d3.svg.line() 238 | .interpolate("monotone") 239 | .x(function(d) { return x(d.date); }) 240 | .y(function(d) { return y(d.amount); }); 241 | 242 | var product = lineChart.selectAll(".product") 243 | .data(products) 244 | .enter().append("g") 245 | .attr("class", "product"); 246 | 247 | product.append("path") 248 | .attr("class", "line") 249 | .attr("d", function(d) { return line(d.values); }) 250 | .style("stroke-width", styles.plot_elements.line.width) 251 | .style("fill", "none") 252 | .style("stroke", function(d) { return color(d.name); }); 253 | 254 | product.append("text") 255 | .datum(function(d) { return {name: d.name, value: d.values[d.values.length - 1]}; }) 256 | .attr("transform", function(d) { return "translate(" + x(d.value.date) + "," + y(d.value.amount) + ")"; }) 257 | .attr("x", 10) 258 | .attr("dy", ".35em") 259 | .style("font-size", styles.text_styles.point_label['font-size']) 260 | .style("font-family", styles.text_styles.point_label['font-family']) 261 | .text(function(d) { return d.name; }) 262 | .on('click', function(d) { 263 | window.replaceHash(d.name); 264 | }); 265 | 266 | var point = product.append("g") 267 | .attr("class", "line-point"); 268 | 269 | point.selectAll("circle") 270 | .data(function(d,i){ return d.values; }) 271 | .enter().append("circle") 272 | .attr("cx", function(d) { return x(d.date); }) 273 | .attr("cy", function(d) { return y(d.amount); }) 274 | .attr("r", 3) 275 | .style("fill", function(d) { return color(d.name); }) 276 | 277 | }; 278 | 279 | /* OBTAINING DATA (uncomment whichever applies) */ 280 | d3.csv('data/' + dataset + '.csv', draw); 281 | //d3.json('data/' + dataset + '.json', draw) 282 | 283 | window.addEventListener("hashchange", function(){ 284 | console.log('Hash changed!'); 285 | d3.csv('data/' + dataset + '.csv', draw); 286 | }); 287 | 288 | $('#doc-dialog').on('hidden.bs.modal', function () { 289 | window.replaceHash(focus.id); 290 | }); 291 | 292 | /* make the embed link work */ 293 | $('#embed-link').on('click', function(evt) { 294 | evt.preventDefault(); 295 | var dialog = $('#embed-dialog'); 296 | dialog.modal('toggle'); 297 | dialog.find('.iframe-src').html(window.location.href); 298 | dialog.find('.iframe-height').html($(window).height()); 299 | dialog.find('.iframe-width').html($(window).width()); 300 | }) 301 | 302 | /* make the new window link work */ 303 | $('#new-link').on('click', function(evt) { 304 | evt.preventDefault(); 305 | window.open(window.location.href); 306 | }) 307 | 308 | /* check see if there's a hash and load it */ 309 | if (window.location.hash) { 310 | var hparts = window.location.hash.slice(1).split("/"); 311 | } 312 | }); 313 | 314 | var formatDate = function(d) { 315 | months = ["January", "February", "March", 316 | "April", "May", "June", "July", "August", "September", 317 | "October", "November", "December"]; 318 | 319 | return months[d.getMonth()] + " " + d.getDate() + ", " + d.getFullYear(); 320 | } 321 | })(jQuery); 322 | -------------------------------------------------------------------------------- /implementations/d3/assets/js/hbar.js: -------------------------------------------------------------------------------- 1 | 2 | d3.json('assets/specs/styles.json', function(error, s) { 3 | 4 | // Parameters for top-level sizing of plot 5 | var blog_or_feature = 'blog'; 6 | var desired_height = 400; 7 | var units = 'units'; 8 | var div_selector = "#hbar-chart"; 9 | var bar_color = "yellow"; 10 | 11 | // TODO: (optionally?) Apply styles dynamically 12 | 13 | d3.json('data/test_data.json', function(error, data) { 14 | 15 | var svg = d3.select(div_selector+' svg'); 16 | 17 | var maxValue = d3.max(data, function(d) { return d.value; }); 18 | 19 | var xFormatter = d3.format(",.0d"); 20 | 21 | /* 22 | * Setting margins according to longest yAxis label, default to styles.json 23 | */ 24 | 25 | // ... get default margins from specs 26 | var margin = s.plot_elements.canvas.margin; 27 | 28 | // ... create invisible text object 29 | var testText = svg.append("g") 30 | .attr("class", "axis") 31 | .append("text") 32 | .attr("class", "test-text") 33 | .attr("y", -1000) 34 | .classed("axis", "true") 35 | .text(function(d){ return xFormatter(maxValue) }); 36 | 37 | testText.append("tspan") 38 | .classed("unit", true) 39 | .text(" " + units); 40 | 41 | // ... measure width of invisible text object 42 | var yLabelHeight = Math.max(testText[0][0].getBoundingClientRect().width,0); 43 | 44 | testText.data([]).exit().remove(); 45 | 46 | // ... use larger of two margins 47 | var suggestedLeftMargin = yLabelHeight + parseInt(s.text_styles.axis_title['font-size']) + 2 + s.plot_elements.axis.title_padding; // plus 2 related to space above/below text 48 | 49 | margin.left = Math.max(margin.left, suggestedLeftMargin); 50 | 51 | // ... follow D3 margin convention as normal 52 | var width = s.plot_elements.canvas.width[blog_or_feature] - margin.left - margin.right, 53 | height = desired_height - margin.top - margin.bottom; 54 | 55 | svg.attr("width", width + margin.left + margin.right) 56 | .attr("height", height + margin.top + margin.bottom); 57 | 58 | /* 59 | * Creating scales 60 | */ 61 | 62 | var y = d3.scale.ordinal() 63 | .rangeRoundBands([0, height], .1) 64 | .domain(data.map(function(d) { return d.label; })); 65 | 66 | var x = d3.scale.linear() 67 | .range([0, width]) 68 | .domain([0, maxValue + 2]); 69 | 70 | /* 71 | * Creating Axes and Gridlines (innerTick) 72 | */ 73 | 74 | var yAxis = d3.svg.axis() 75 | .scale(y) 76 | .innerTickSize(-width) // really long ticks become gridlines 77 | .outerTickSize(0) 78 | .tickPadding(5) 79 | .orient("left"); 80 | 81 | var xAxis = d3.svg.axis() 82 | .scale(x) 83 | .orient("bottom") 84 | .innerTickSize(-height) // really long ticks become gridlines 85 | .outerTickSize(0) 86 | .tickPadding(5) 87 | .tickValues([10, 20, 30]) // setting tick values explicitly 88 | .tickFormat(xFormatter); 89 | 90 | /* 91 | * Drawing chart 92 | */ 93 | 94 | var basicChart = svg.append("g") 95 | .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); 96 | // ... add x axis 97 | basicChart.append("g") 98 | .attr("class", "x axis") 99 | .attr("transform", "translate(0," + height + ")") 100 | .call(xAxis) 101 | .append("text") 102 | .classed("title",true) 103 | .attr("x", function() { return (width / 2.0);}) // anchors title in middle of chart 104 | .attr("y", function() { return (margin.bottom);}) 105 | .style("text-anchor", "middle") // centers title around anchor 106 | .text("X Axis Title"); 107 | 108 | // ... add y axis with value labels 109 | basicChart.append("g") 110 | .attr("class", "y axis") 111 | .call(yAxis) 112 | .append("text") 113 | .classed("title",true) 114 | .attr("transform", "rotate(-90)") 115 | .attr("x", function() { return -(height / 2.0);}) 116 | .attr("y", function() { return -(margin.left);}) 117 | .attr("dy", function() { return (parseInt(s.text_styles.axis_label['font-size'])-2); }) // minus 2 related to space above/below text 118 | .style("text-anchor", "middle") 119 | .text("Y Axis Title"); 120 | 121 | // ... add value units to value labels in separate span for distinct styling 122 | d3.select(div_selector + ' .x.axis') 123 | .selectAll('.tick') 124 | .select('text') 125 | .append("tspan") 126 | .classed("unit", true) 127 | .text(" " + units); 128 | 129 | // ... add bars using bar_color (set above) for fill 130 | basicChart.selectAll(".bar") 131 | .data(data) 132 | .enter().append("rect") 133 | .attr("class","bar") 134 | .attr("y", function(d) { return y(d.label); }) 135 | .attr("height", y.rangeBand()) 136 | .attr("x", function(d) { return 0; }) 137 | .attr("width", function(d) { return x(d.value); }) 138 | .style("fill", function(d) { return s.colors.data.main[bar_color].hex;} ); 139 | }); 140 | 141 | }); 142 | -------------------------------------------------------------------------------- /implementations/d3/assets/js/line.js: -------------------------------------------------------------------------------- 1 | d3.json('assets/specs/styles.json', function(error, s) { 2 | 3 | // Parameters for top-level sizing of plot 4 | var blog_or_feature = 'blog'; 5 | var desired_height = 400; 6 | var div_selector = "#line-chart"; 7 | 8 | var colors = { "republican" : s.colors.data.parties.republican.hex, 9 | "democrat" : s.colors.data.parties.democrat.hex } 10 | 11 | // TODO: (optionally?) Apply styles dynamically 12 | 13 | d3.json('data/test_data_line.json', function(error, data) { 14 | 15 | var svg = d3.select(div_selector+' svg'); 16 | 17 | 18 | var yFormatter = d3.format(",.0$"); 19 | 20 | var parseDate = d3.time.format("%Y").parse; 21 | 22 | // convert time input to d3 time objects 23 | data.forEach(function(d) { 24 | d.date = parseDate(d.year); 25 | }); 26 | 27 | // pivot data 28 | var parties = d3.keys(colors).map(function(party) { 29 | return { 30 | party: party, 31 | values: data.map(function(d) { 32 | return {date: d.date, amount: +d[party], party: party}; 33 | }) 34 | }; 35 | }); 36 | 37 | console.log(parties); 38 | 39 | var maxValue = d3.max(parties, function(c) { return d3.max(c.values, function(v) { return v.amount; }); }); 40 | 41 | /* 42 | * Setting margins according to longest yAxis label, default to styles.json 43 | */ 44 | 45 | // ... get default margins from specs 46 | var margin = s.plot_elements.canvas.margin; 47 | 48 | // ... create invisible text object 49 | var testText = svg.append("g") 50 | .attr("class", "axis") 51 | .append("text") 52 | .attr("class", "test-text") 53 | .attr("x", -1000) 54 | .classed("axis", "true") 55 | .text(function(d){ return "$" + yFormatter(maxValue); }); 56 | 57 | // ... measure width of invisible text object 58 | var yLabelWidth = Math.max(testText[0][0].getBoundingClientRect().width,0); 59 | 60 | testText.data([]).exit().remove(); 61 | 62 | // ... use larger of two margins 63 | var suggestedLeftMargin = yLabelWidth + parseInt(s.text_styles.axis_title['font-size']) + 2 + s.plot_elements.axis.title_padding; // plus 2 related to space above/below text 64 | 65 | margin.left = Math.max(margin.left, suggestedLeftMargin); 66 | 67 | // ... follow D3 margin convention as normal 68 | var width = s.plot_elements.canvas.width[blog_or_feature] - margin.left - margin.right, 69 | height = desired_height - margin.top - margin.bottom; 70 | 71 | svg.attr("width", width + margin.left + margin.right) 72 | .attr("height", height + margin.top + margin.bottom); 73 | 74 | /* 75 | * Creating scales 76 | */ 77 | 78 | var x = d3.time.scale() 79 | .range([0, width]); 80 | 81 | var y = d3.scale.linear() 82 | .range([height, 0]); 83 | 84 | x.domain([ 85 | d3.min(data, function(d) { return d3.time.year.offset(d.date, -5);}), 86 | d3.max(data, function(d) { return d3.time.year.offset(d.date, 1);}) 87 | ]); 88 | 89 | y.domain([ 90 | // Style guide says to start at zero 91 | //d3.min(parties, function(c) { return d3.min(c.values, function(v) { return v.amount; }); }), 92 | 0, 93 | d3.max(parties, function(c) { return d3.max(c.values, function(v) { return v.amount; }); }) + 100 94 | ]); 95 | 96 | /* 97 | * Creating Axes and Gridlines (innerTick) 98 | */ 99 | 100 | var xAxis = d3.svg.axis() 101 | .scale(x) 102 | .ticks(d3.time.year, 10) 103 | .innerTickSize(-height) // really long ticks become gridlines 104 | .outerTickSize(0) 105 | .tickPadding(5) 106 | .orient("bottom"); 107 | 108 | var yAxis = d3.svg.axis() 109 | .scale(y) 110 | .orient("left") 111 | .innerTickSize(-width) // really long ticks become gridlines 112 | .outerTickSize(0) 113 | .tickPadding(5) 114 | .tickFormat(function(d){ return "$"+yFormatter(d);}); 115 | 116 | /* 117 | * Drawing chart 118 | */ 119 | 120 | var lineChart = svg.append("g") 121 | .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); 122 | 123 | // ... add x axis 124 | lineChart.append("g") 125 | .attr("class", "x axis") 126 | .attr("transform", "translate(0," + height + ")") 127 | .call(xAxis) 128 | .append("text") 129 | .classed("title",true) 130 | .attr("x", function() { return (width / 2.0);}) // anchors title in middle of chart 131 | .attr("y", function() { return (margin.bottom);}) 132 | .style("text-anchor", "middle") // centers title around anchor 133 | .text("X Axis Title"); 134 | 135 | // ... add y axis with value labels 136 | lineChart.append("g") 137 | .attr("class", "y axis") 138 | .call(yAxis) 139 | .append("text") 140 | .classed("title",true) 141 | .attr("transform", "rotate(-90)") 142 | .attr("x", function() { return -(height / 2.0);}) 143 | .attr("y", function() { return -(margin.left);}) 144 | .attr("dy", function() { return (parseInt(s.text_styles.axis_label['font-size'])-2); }) // minus 2 related to space above/below text 145 | .style("text-anchor", "middle") 146 | .text("Y Axis Title"); 147 | 148 | // line drawing function 149 | var line = d3.svg.line() 150 | //.interpolate("basis") 151 | .x(function(d) { return x(d.date); }) 152 | .y(function(d) { return y(d.amount); }); 153 | 154 | var party = lineChart.selectAll(".party") 155 | .data(parties) 156 | .enter().append("g") 157 | .attr("class", "party"); 158 | 159 | party.append("path") 160 | .attr("class", "line") 161 | .attr("d", function(d) { return line(d.values); }) 162 | .style("stroke-width", s.plot_elements.line.width) 163 | .style("fill", "none") 164 | .style("stroke", function(d) { return colors[d.party]; }); 165 | 166 | var point = party.append("g") 167 | .attr("class", "line-point"); 168 | 169 | point.selectAll("circle") 170 | .data(function(d,i){ return d.values; }) 171 | .enter().append("circle") 172 | .attr("cx", function(d) { return x(d.date); }) 173 | .attr("cy", function(d) { return y(d.amount); }) 174 | .attr("r", 3) 175 | .style("fill", function(d) { return colors[d.party]; }) 176 | 177 | var point_labels = party.append("g") 178 | .attr("class", "point-label"); 179 | 180 | point_labels.selectAll("text") 181 | .data(function(d,i){ return d.values; }) 182 | .enter().append("text") 183 | .style("fill", s.text_styles.point_label.color) 184 | .style("font-size", s.text_styles.point_label['font-size']) 185 | .style("font-family", s.text_styles.point_label['font-family']) 186 | .attr("x", function(d) { return x(d.date); }) 187 | .attr("y", function(d) { return y(d.amount); }) 188 | //.attr("dx", function(d) { return -(longestLabel); }) 189 | .style("text-anchor","end") 190 | .attr("dy", function(d) { return -(parseInt(s.text_styles.point_label['font-size']) / 3);}) 191 | .attr("dx", -5) 192 | .text(function(d) { return "$" + yFormatter(d.amount); }) 193 | 194 | // TODO: Add Legend 195 | 196 | }); 197 | 198 | }); 199 | -------------------------------------------------------------------------------- /implementations/d3/assets/js/pie.js: -------------------------------------------------------------------------------- 1 | d3.json('assets/specs/styles.json', function(error, s) { 2 | 3 | console.warn(error); 4 | 5 | // Parameters for top-level sizing of plot 6 | var blog_or_feature = 'blog'; 7 | var desired_height = 400; 8 | var units = 'units'; 9 | var div_selector = "#pie-chart"; 10 | var rotate_angle = 70; 11 | 12 | var colors = { "pro": s.colors.data.pro_con.pro.hex, 13 | "con": s.colors.data.pro_con.con.hex, 14 | "other": s.colors.data.main.neutral.hex } 15 | 16 | console.log(colors); 17 | 18 | // TODO: (optionally?) Apply styles dynamically 19 | 20 | d3.json('data/test_data_pie.json', function(error, data) { 21 | 22 | var svg = d3.select(div_selector+' svg'); 23 | 24 | var yFormatter = d3.format(",.0d"); 25 | 26 | /* 27 | * Setting margins according to longest yAxis label, default to styles.json 28 | */ 29 | 30 | // ... get default margins from specs 31 | var margin = s.plot_elements.canvas.margin.pie; 32 | 33 | var width = s.plot_elements.canvas.width[blog_or_feature] - margin.left - margin.right, 34 | height = desired_height - margin.top - margin.bottom; 35 | 36 | svg.attr("width", width + margin.left + margin.right) 37 | .attr("height", height + margin.top + margin.bottom); 38 | 39 | /* 40 | * Creating arc 41 | */ 42 | 43 | var radius = Math.min(width, height) / 2; 44 | 45 | var arc = d3.svg.arc() 46 | .outerRadius(radius) 47 | .innerRadius(0); 48 | 49 | var pie = d3.layout.pie() 50 | .sort(null) 51 | .value(function(d) { return d.percentage; }); 52 | 53 | /* 54 | * Drawing chart 55 | */ 56 | 57 | var pieChartContainer = svg.append("g") 58 | .attr("transform", "translate(" + width / 3 + "," + height / 2 + ")"); 59 | 60 | labelContainer = pieChartContainer.append("g") 61 | .classed("pie-labels","true"); 62 | 63 | labels = labelContainer.selectAll(".pie-label") 64 | .data(pie(data)) 65 | .enter().append("g") 66 | .classed("pie-label", true); 67 | 68 | labels.append("text") 69 | .attr("transform", function(d) { 70 | var c = arc.centroid(d), 71 | x = c[0], 72 | y = c[1], 73 | h = Math.sqrt(x*x + y*y); 74 | 75 | label_line = width * 1/2; 76 | 77 | label_x = x + (label_line - x); 78 | 79 | return "translate(" + label_x + "," + y + ")"; 80 | }) 81 | .style("font-family", s.text_styles.axis_label['font-family']) 82 | .style("font-size", s.text_styles.axis_label['font-size']) 83 | .style("fill", s.text_styles.axis_label['color']) 84 | .style("stroke", "none") 85 | //.attr("dy", ".35em") 86 | .attr("text-anchor", "start") 87 | /*function(d) { 88 | return (d.endAngle + d.startAngle) / 2 > Math.PI ? "end" : "start"; 89 | })*/ 90 | .text(function(d) { return d.data.label; }) 91 | .append("tspan") 92 | .style("font-family", s.text_styles.axis_title['font-family']) 93 | .text(function(d) { return " (" + d.value + "%)"; }); 94 | 95 | //var textBoxes 96 | 97 | labels.each(function(d,i) { 98 | if(i > 0) { 99 | var textOffset = 0; 100 | 101 | var thisbb = this.getBoundingClientRect(), 102 | prevbb = prev.getBoundingClientRect(); 103 | console.log(thisbb); 104 | console.log(prevbb); 105 | 106 | if(!(thisbb.right < prevbb.left || 107 | thisbb.left > prevbb.right || 108 | thisbb.bottom < prevbb.top || 109 | thisbb.top > prevbb.bottom)) { 110 | console.log("clash:"); 111 | console.log(thisbb); 112 | console.log(prevbb); 113 | var ctx = thisbb.left + (thisbb.right - thisbb.left)/2, 114 | cty = thisbb.top + (thisbb.bottom - thisbb.top)/2, 115 | cpx = prevbb.left + (prevbb.right - prevbb.left)/2, 116 | cpy = prevbb.top + (prevbb.bottom - prevbb.top)/2, 117 | off = Math.sqrt(Math.pow(ctx - cpx, 2) + Math.pow(cty - cpy, 2))/2; 118 | d3.select(this).attr("transform","translate(0,20)"); 119 | } 120 | } 121 | prev = this; 122 | }); 123 | 124 | labels.append("path") 125 | .attr("d", function(d) { 126 | var label_line = width * 1/2; 127 | return "M 0 1 H -" + label_line; 128 | }) 129 | .attr("transform", function(d) { 130 | var c = arc.centroid(d), 131 | x = c[0], 132 | y = c[1], 133 | h = Math.sqrt(x*x + y*y); 134 | 135 | label_line = width * 1/2; 136 | 137 | label_x = x + (label_line - x); 138 | 139 | return "translate(" + label_x + "," + y + ")"; 140 | }) 141 | .style("stroke-width", s.plot_elements.indicator.line['width']) 142 | .style("stroke", s.plot_elements.indicator.line.color); 143 | 144 | var pieChart = pieChartContainer.append("g") 145 | .attr("transform", "rotate("+rotate_angle+")"); 146 | 147 | var arcs = pieChart.selectAll(".arc") 148 | .data(pie(data)) 149 | .enter().append("g") 150 | .attr("class", "arc"); 151 | 152 | arcs.append("path") 153 | .attr("d", arc) 154 | .style("stroke", s.plot_elements.pie.stroke) 155 | .style("fill", function(d) { 156 | return colors[d.data.label]; 157 | }); 158 | 159 | }); 160 | 161 | }); 162 | -------------------------------------------------------------------------------- /implementations/d3/assets/js/widget.js: -------------------------------------------------------------------------------- 1 | // from http://stackoverflow.com/questions/9235304/how-to-replace-the-location-hash-and-only-keep-the-last-history-entry 2 | (function(namespace) { // Closure to protect local variable "var hash" 3 | if ('replaceState' in history) { // Yay, supported! 4 | namespace.replaceHash = function(newhash) { 5 | if ((''+newhash).charAt(0) !== '#') newhash = '#' + newhash; 6 | history.replaceState('', '', newhash); 7 | } 8 | } else { 9 | var hash = location.hash; 10 | namespace.replaceHash = function(newhash) { 11 | if (location.hash !== hash) history.back(); 12 | location.hash = newhash; 13 | }; 14 | } 15 | })(window); 16 | 17 | (function($) { 18 | // grab the query dict and see if this is a special one 19 | var queryDict = {}; 20 | location.search.substr(1).split("&").forEach(function(item) {queryDict[item.split("=")[0]] = item.split("=")[1]}); 21 | var dataset = queryDict.t ? queryDict.t : 'data'; 22 | 23 | var stylesXHR = $.getJSON('assets/specs/styles.json'); 24 | 25 | $.when(stylesXHR).done(function(stylesResult) { 26 | styles = stylesResult[0]; 27 | 28 | var $svg = $('svg'); 29 | 30 | /* GETTING SIZE FROM CONTEXT */ 31 | var margin = 20, 32 | width = $svg.width(), 33 | height = $svg.height(); 34 | 35 | var focus; 36 | 37 | /* USEFUL UTILS */ 38 | 39 | /* D3 formats */ 40 | var format = d3.format("0,000"); 41 | var percentFormat = d3.format(".4p"); 42 | 43 | /* opening the modal */ 44 | var openModalDetail = function(evt) { 45 | evt.stopPropagation(); 46 | evt.preventDefault(); 47 | 48 | var dialog = $('#doc-dialog'); 49 | dialog.modal('toggle'); 50 | 51 | window.replaceHash(focus.id); 52 | 53 | /* Add stuff to the modal dialog here */ 54 | 55 | }; 56 | 57 | var setFocus = function(d) { 58 | var focus0 = focus; 59 | focus = d; 60 | }; 61 | 62 | var draw = function(d) { 63 | 64 | /* YOUR CODE HERE */ 65 | console.log(d[0]); 66 | 67 | }; 68 | 69 | /* OBTAINING DATA (uncomment whichever applies) */ 70 | d3.csv('data/' + dataset + '.csv', draw); 71 | //d3.json('data/' + dataset + '.json', draw) 72 | 73 | 74 | $('#doc-dialog').on('hidden.bs.modal', function () { 75 | window.replaceHash(focus.id); 76 | }); 77 | 78 | /* make the embed link work */ 79 | $('#embed-link').on('click', function(evt) { 80 | evt.preventDefault(); 81 | var dialog = $('#embed-dialog'); 82 | dialog.modal('toggle'); 83 | dialog.find('.iframe-src').html(window.location.href); 84 | dialog.find('.iframe-height').html($(window).height()); 85 | dialog.find('.iframe-width').html($(window).width()); 86 | }) 87 | 88 | /* make the new window link work */ 89 | $('#new-link').on('click', function(evt) { 90 | evt.preventDefault(); 91 | window.open(window.location.href); 92 | }) 93 | 94 | /* check see if there's a hash and load it */ 95 | if (window.location.hash) { 96 | var hparts = window.location.hash.slice(1).split("/"); 97 | } 98 | }); 99 | 100 | var formatDate = function(d) { 101 | months = ["January", "February", "March", 102 | "April", "May", "June", "July", "August", "September", 103 | "October", "November", "December"]; 104 | 105 | return months[d.getMonth()] + " " + d.getDate() + ", " + d.getFullYear(); 106 | } 107 | })(jQuery); 108 | -------------------------------------------------------------------------------- /implementations/d3/assets/specs: -------------------------------------------------------------------------------- 1 | ../../../specs -------------------------------------------------------------------------------- /implementations/d3/data/cfpb_lines.csv: -------------------------------------------------------------------------------- 1 | quarter,Debt collection,Mortgage,Credit card,Bank account or service,Credit reporting,Student loan,Consumer loan,Payday loan 2 | 20120101,0,7388,3768,1198,0,524,142,0 3 | 20120401,0,10893,4346,4325,0,996,651,0 4 | 20120701,0,10487,3793,3651,0,645,591,0 5 | 20121001,0,9449,3479,3071,1873,677,607,0 6 | 20130101,0,15239,3585,3523,3078,740,697,0 7 | 20130401,0,13470,3308,3075,3470,622,770,0 8 | 20130701,4410,11776,3207,3429,3953,761,817,0 9 | 20131001,6666,8947,3010,3357,3888,882,834,194 10 | 20140101,10251,11665,3691,3731,7485,1108,1064,392 11 | 20140401,10296,10926,3467,3741,7320,1064,1059,349 12 | 20140701,9622,10693,3563,3709,7801,1060,1655,505 13 | 20141001,9025,9733,3259,3497,6638,1054,1683,461 14 | 20150101,10610,9636,3996,3683,7888,1162,1837,458 15 | -------------------------------------------------------------------------------- /implementations/d3/data/post_debt.csv: -------------------------------------------------------------------------------- 1 | Company,Debt collection,Mortgage,Credit reporting,Credit card,Bank account or service,Consumer loan,Student loan,Payday loan 2 | AES/PHEAA,120,0,0,0,3,1,863,0 3 | Ally Financial Inc.,133,22,8,5,329,833,3,0 4 | Amex,181,2,19,2627,173,8,0,0 5 | Bank of America,860,20358,22,3561,5818,282,30,4 6 | Barclays,108,1,11,1175,51,3,0,0 7 | "Bayview Loan Servicing, LLC",60,875,0,0,2,1,0,7 8 | BB&T Financial,58,417,5,59,675,138,0,0 9 | Capital One,1069,449,141,5043,995,534,0,1 10 | Citibank,1463,4689,104,6102,1148,421,215,14 11 | Discover,277,36,27,1704,138,35,393,0 12 | Encore Capital Group,4382,5,61,75,16,29,1,1 13 | "Enhanced Recovery Company, LLC",1802,0,22,3,5,3,0,1 14 | Equifax,10,11,19936,17,9,16,1,0 15 | Experian,30,17,20333,29,10,10,2,1 16 | "Expert Global Solutions, Inc.",1452,0,14,21,9,22,149,2 17 | Fifth Third Bank,90,532,9,161,653,159,0,1 18 | Flagstar Bank,6,935,3,1,42,1,0,0 19 | GE Capital Retail,1057,19,49,3759,293,286,1,36 20 | "Green Tree Servicing, LLC",363,5243,9,0,16,38,0,1 21 | HSBC,615,2332,34,344,393,94,4,1 22 | JPMorgan Chase,875,9372,42,3889,3898,390,460,2 23 | KeyBank NA,41,134,4,56,301,31,203,0 24 | M&T Bank,32,861,3,27,418,66,1,0 25 | Nationstar Mortgage,179,8924,8,0,16,7,0,0 26 | Navient,803,5,7,0,9,10,4060,0 27 | Navy FCU,66,195,13,147,339,141,0,0 28 | Ocwen,333,13098,23,4,41,16,0,0 29 | OneWest Bank,15,779,2,1,42,1,0,0 30 | PayPal,169,1,2,243,283,40,0,13 31 | PHH Mortgage,13,814,3,0,1,0,0,0 32 | PNC Bank,88,2153,12,198,1484,134,39,1 33 | "Portfolio Recovery Associates, Inc.",2047,5,25,32,5,16,1,1 34 | RBS Citizens,43,540,2,91,828,80,39,1 35 | Regions,18,254,4,92,836,54,0,2 36 | Resurgent Capital Services L.P.,1119,124,38,32,11,10,2,1 37 | Santander Bank US,15,345,1,53,692,54,0,1 38 | Santander Consumer USA,159,6,9,2,34,982,0,1 39 | "Select Portfolio Servicing, Inc",108,2739,2,0,6,0,0,0 40 | Seterus,31,1427,2,0,4,1,0,0 41 | Specialized Loan Servicing LLC,40,1097,3,0,6,1,0,0 42 | SunTrust Bank,45,1344,6,82,1034,119,13,0 43 | TD Bank,145,345,11,546,1452,196,0,0 44 | TransUnion,19,9,15519,8,5,5,0,1 45 | USAA Savings,58,174,8,301,481,93,0,0 46 | U.S. Bancorp,254,2398,23,1059,1645,246,35,10 47 | Wells Fargo,679,14273,65,1619,5751,959,562,9 48 | -------------------------------------------------------------------------------- /implementations/d3/data/pre_debt.csv: -------------------------------------------------------------------------------- 1 | Company,Debt collection,Mortgage,Credit reporting,Credit card,Bank account or service,Consumer loan,Student loan,Payday loan 2 | AES/PHEAA,0,0,0,0,0,0,346,0 3 | Ally Financial Inc.,0,0,0,4,105,116,0,0 4 | Amex,0,2,1,972,27,0,0,0 5 | Bank of America,0,12222,9,2254,2041,127,28,0 6 | Barclays,0,1,0,518,7,2,0,0 7 | "Bayview Loan Servicing, LLC",0,101,0,0,0,0,1,0 8 | BB&T Financial,0,157,0,26,184,18,0,0 9 | Capital One,0,215,9,3314,443,158,1,0 10 | Citibank,0,1848,6,3228,357,88,225,0 11 | Discover,0,3,0,780,35,4,81,0 12 | Encore Capital Group,0,0,0,0,0,0,0,0 13 | "Enhanced Recovery Company, LLC",0,0,0,0,0,0,0,0 14 | Equifax,0,5,616,4,1,1,0,0 15 | Experian,0,3,720,4,2,1,0,0 16 | "Expert Global Solutions, Inc.",0,0,0,0,0,0,0,0 17 | Fifth Third Bank,0,255,0,95,225,53,0,0 18 | Flagstar Bank,0,469,0,0,15,1,0,0 19 | GE Capital Retail,0,8,2,1289,72,50,0,0 20 | "Green Tree Servicing, LLC",0,828,0,0,1,2,0,0 21 | HSBC,0,910,1,86,237,46,1,0 22 | JPMorgan Chase,0,3903,4,2168,1323,134,162,0 23 | KeyBank NA,0,59,0,3,140,15,119,0 24 | M&T Bank,0,272,0,17,132,29,0,0 25 | Nationstar Mortgage,0,955,0,0,0,0,0,0 26 | Navient,0,0,0,0,1,4,1206,0 27 | Navy FCU,0,85,0,36,72,25,0,0 28 | Ocwen,0,3814,0,1,5,3,0,0 29 | OneWest Bank,0,413,1,0,23,0,0,0 30 | PayPal,0,0,0,0,0,0,0,0 31 | PHH Mortgage,0,272,0,0,0,0,0,0 32 | PNC Bank,0,686,1,93,597,46,12,0 33 | "Portfolio Recovery Associates, Inc.",0,0,0,0,0,0,0,0 34 | RBS Citizens,0,256,1,58,438,40,24,0 35 | Regions,0,103,0,46,284,15,0,0 36 | Resurgent Capital Services L.P.,0,7,0,9,0,0,0,0 37 | Santander Bank US,0,136,0,7,272,24,0,0 38 | Santander Consumer USA,0,3,1,2,1,140,0,0 39 | "Select Portfolio Servicing, Inc",0,262,0,0,0,0,0,0 40 | Seterus,0,342,0,0,1,0,0,0 41 | Specialized Loan Servicing LLC,0,184,0,0,1,0,0,0 42 | SunTrust Bank,0,740,2,43,326,27,12,0 43 | TD Bank,0,156,0,61,483,43,1,0 44 | TransUnion,0,0,468,0,0,0,0,0 45 | USAA Savings,0,95,1,123,128,16,0,0 46 | U.S. Bancorp,0,676,3,344,482,69,26,0 47 | Wells Fargo,0,6175,5,703,2277,376,214,0 48 | -------------------------------------------------------------------------------- /implementations/d3/data/test_data.json: -------------------------------------------------------------------------------- 1 | [ 2 | { "label": "Label 1", "value": 10 }, 3 | { "label": "Label 2", "value": 30 }, 4 | { "label": "Label 3", "value": 20 }, 5 | { "label": "Label 4", "value": 30 } 6 | ] 7 | -------------------------------------------------------------------------------- /implementations/d3/data/test_data_line.json: -------------------------------------------------------------------------------- 1 | [ 2 | { "year": "2000", "republican": 1090, "democrat": 590 }, 3 | { "year": "2010", "republican": 2100, "democrat": 710 }, 4 | { "year": "2020", "republican": 3030, "democrat": 930 } 5 | ] 6 | -------------------------------------------------------------------------------- /implementations/d3/data/test_data_pie.json: -------------------------------------------------------------------------------- 1 | [ 2 | { "label": "pro", "percentage": 94 }, 3 | { "label": "con", "percentage": 5 }, 4 | { "label": "other", "percentage": 1 } 5 | ] 6 | -------------------------------------------------------------------------------- /implementations/d3/embeddable.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | 12 | 13 | 23 | 24 | 25 |
26 |
27 |
28 |
29 |
30 | Title 31 |
32 |
33 | Subtitle 34 |
35 |
36 | 38 |
39 |
40 |
41 | 42 |
43 | 44 | 45 |
46 | 47 | 56 | 57 | 58 | 59 | 60 | 74 | 75 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /implementations/d3/embedding.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /implementations/d3/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |

Basic Structure

10 |
11 |
12 |
13 |
14 |
15 | Title of the chart 16 |
17 |
18 | For explanatory text that's not very long. 19 |
20 |
21 | 23 |
24 |
25 |
26 | 27 | 28 |
29 |
30 | 35 |
36 |

Bar Chart

37 |
38 |
39 |
40 |
41 |
42 | Title of the chart 43 |
44 |
45 | For explanatory text that's not very long. 46 |
47 |
48 | 50 |
51 |
52 |
53 | 54 | 55 |
56 |
57 | 62 |
63 |

Horizontal Bar Chart

64 |
65 |
66 |
67 |
68 |
69 | Title of the chart 70 |
71 |
72 | For explanatory text that's not very long. 73 |
74 |
75 | 77 |
78 |
79 |
80 | 81 | 82 |
83 |
84 | 89 |
90 |

Line Chart

91 |
92 |
93 |
94 |
95 |
96 | Title of the chart 97 |
98 |
99 | For explanatory text that's not very long. 100 |
101 |
102 | 104 |
105 |
106 |
107 | 108 | 109 |
110 |
111 | 116 |
117 |

Pie Chart

118 |
119 |
120 |
121 |
122 |
123 | Title of the chart 124 |
125 |
126 | For explanatory text that's not very long. 127 |
128 |
129 | 131 |
132 |
133 |
134 | 135 | 136 |
137 |
138 | 143 |
144 | 145 |

CFPB Chart

146 |
147 |
148 |
149 |
150 |
151 | CFPB Complaint Volume 152 |
153 |
154 | By financial product 155 |
156 |
157 | 159 |
160 |
161 |
162 | 163 | 164 |
165 |
166 | 171 |
172 | 173 | 174 |

CFPB Chart

175 |
176 |
177 |
178 |
179 |
180 | CFPB Complaint Volume 181 |
182 |
183 | By financial product 184 |
185 |
186 | 188 |
189 |
190 |
191 | 192 | 193 |
194 |
195 | 200 |
201 | 202 |

CFPB Chart

203 |
204 |
205 |
206 |
207 |
208 | CFPB Complaint Volume 209 |
210 |
211 | By financial product 212 |
213 |
214 | 216 |
217 |
218 |
219 | 220 | 221 |
222 |
223 | 228 |
229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | -------------------------------------------------------------------------------- /specs/assets/fonts.example.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunlightlabs/chartoff/7ec5429d986f7cbf5bfc262967b0b2edaf5d9699/specs/assets/fonts.example.zip -------------------------------------------------------------------------------- /specs/docs/Sunlight-StyleGuide-DataViz.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunlightlabs/chartoff/7ec5429d986f7cbf5bfc262967b0b2edaf5d9699/specs/docs/Sunlight-StyleGuide-DataViz.pdf -------------------------------------------------------------------------------- /specs/styles.json: -------------------------------------------------------------------------------- 1 | { 2 | "basic_structure": 3 | { 4 | "container": 5 | { 6 | "width": 7 | { 8 | "blog": 650, 9 | "feature": 900 10 | }, 11 | "padding": 12 | { 13 | "top": 12 14 | }, 15 | "background": 16 | { 17 | "color": "#EFECEA", 18 | "color_ref": ["colors", "background", "default"] 19 | } 20 | }, 21 | "header": 22 | { 23 | "container": 24 | { 25 | "width": "100%", 26 | "padding": 27 | { 28 | "left": 22, 29 | "right": 22 30 | }, 31 | "background": 32 | { 33 | "color": "#F5F3F2", 34 | "color_ref": ["colors", "background", "light"] 35 | } 36 | }, 37 | "title": 38 | { 39 | "float":"left" 40 | }, 41 | "logo": 42 | { 43 | "width": 100, 44 | "float": "right" 45 | } 46 | }, 47 | "chart_area": 48 | { 49 | "width": "100%", 50 | "padding": 51 | { 52 | "top": 22, 53 | "right": 0, 54 | "left": 22, 55 | "bottom": 22 56 | } 57 | }, 58 | "divider": 59 | { 60 | "solid": 61 | { 62 | "size": 1, 63 | "color": "#FFFFFF", 64 | "color_ref": ["colors", "line", "white"] 65 | }, 66 | "dashed": 67 | { 68 | "size": 1, 69 | "color": "#C0C0BB", 70 | "color_ref": ["colors", "line", "grey"], 71 | "margin": 72 | { 73 | "top": 1, 74 | "bottom": 1 75 | } 76 | } 77 | }, 78 | "footer": 79 | { 80 | "container" : 81 | { 82 | "width": "100%", 83 | "height": 22, 84 | "padding": 85 | { 86 | "left": 22, 87 | "right": 22 88 | }, 89 | "background": 90 | { 91 | "color": "#E5E2E0", 92 | "color_ref": ["colors", "background", "dark"] 93 | } 94 | } 95 | } 96 | }, 97 | 98 | "plot_elements": 99 | { 100 | "canvas": 101 | { 102 | "width": 103 | { 104 | "blog": 622, 105 | "feature": 878 106 | }, 107 | "margin": 108 | { 109 | "top": 22, 110 | "left": 63, 111 | "bottom": 51, 112 | "right": 22, 113 | 114 | "default": 115 | { 116 | "top": 22, 117 | "left": 63, 118 | "bottom": 51, 119 | "right": 22 120 | }, 121 | 122 | "pie": 123 | { 124 | "top": 0, 125 | "left": 0, 126 | "bottom": 0, 127 | "right": 0 128 | } 129 | } 130 | }, 131 | "axis": 132 | { 133 | "width": 1, 134 | "color": "#FFFFFF", 135 | "label_padding": 5, 136 | "title_padding": 22, 137 | "break": 138 | { 139 | "height": 5, 140 | "width": 5 141 | }, 142 | "color_ref": ["colors", "line", "white"] 143 | }, 144 | "grid_line": 145 | { 146 | "solid": 147 | { 148 | "width": 1, 149 | "color": "#FFFFFF", 150 | "color_ref": ["colors", "line", "white"] 151 | }, 152 | "dashed": 153 | { 154 | "width": 1, 155 | "color": "#C0C0BB", 156 | "color_ref": ["colors", "line", "grey"], 157 | "margin": 158 | { 159 | "top": 1, 160 | "bottom": 1 161 | } 162 | } 163 | }, 164 | "pie": 165 | { 166 | "stroke": "#FFFFFF", 167 | "stroke_color_ref": ["colors", "line", "white"] 168 | }, 169 | "legend": 170 | { 171 | "background": 172 | { 173 | "color": "#E5E2E0", 174 | "color_ref": ["colors", "background", "dark"] 175 | }, 176 | "border": 177 | { 178 | "width": 1, 179 | "color": "#C0C0BB", 180 | "color_ref": ["colors", "line", "grey"] 181 | } 182 | }, 183 | "indicator": 184 | { 185 | "line": 186 | { 187 | "width": 1, 188 | "color": "#C0C0BB", 189 | "color_ref": ["colors", "line", "grey"], 190 | "stroke-width": 1, 191 | "stroke": "#C0C0BB", 192 | "stroke_ref": ["colors","line", "grey"] 193 | }, 194 | "shape": 195 | { 196 | "background": "#FFFFFF", 197 | "background_ref": ["colors", "line", "white"], 198 | "border-width": 1, 199 | "border-color": "#C0C0BB", 200 | "border-color_ref": ["colors", "line", "grey"], 201 | "fill": "#FFFFFF", 202 | "fill_ref": ["colors", "line", "white"], 203 | "stroke": "#C0C0BB", 204 | "stroke_ref": ["colors", "line", "grey"], 205 | "stroke-width": 1 206 | } 207 | }, 208 | "line": 209 | { 210 | "width": 2, 211 | "stroke-width": 2 212 | }, 213 | "trend_line": 214 | { 215 | "width": 1, 216 | "color": "#C0C0BB", 217 | "color_ref": ["colors", "line", "grey"], 218 | "stroke-width":1, 219 | "stroke": "#C0C0BB", 220 | "stroke_ref": ["colors", "line", "grey"] 221 | } 222 | }, 223 | 224 | "text_styles": 225 | { 226 | "header_title" : 227 | { 228 | "font-family" : "\"Franklin Gothic Book\", \"Franklin Gothic Medium\", \"Franklin Gothic\", \"ITC Franklin Gothic\", Arial, sans-serif", 229 | "font-size" : "20px", 230 | "font-weight": "normal", 231 | "font-style": "normal", 232 | "text-align" : "left", 233 | "color" : "#635F5D", 234 | "color_ref" : ["colors", "text", "main"] 235 | }, 236 | "header_title_emphatic" : 237 | { 238 | "font-family": "\"Franklin Gothic Demi Cond\", \"Franklin Gothic Medium Cond\", \"Franklin Gothic\", \"ITC Franklin Gothic\", Arial, sans-serif", 239 | "font-size" : "20px", 240 | "font-weight": "normal", 241 | "font-style": "normal", 242 | "text-align" : "left", 243 | "color" : "#635F5D", 244 | "color_ref" : ["colors", "text", "main"] 245 | }, 246 | "header_title_explanatory": 247 | { 248 | "font-family": "font-family: Georgia, Times, \"Times New Roman\", serif", 249 | "font-size" : "12px", 250 | "font-weight": "normal", 251 | "font-style": "italic", 252 | "text-align" : "left", 253 | "color" : "#8E8883", 254 | "color_ref" : ["colors", "text", "light"] 255 | }, 256 | "axis_label" : 257 | { 258 | "font-family": "\"Franklin Gothic Demi Cond\", \"Franklin Gothic Medium Cond\", \"Franklin Gothic\", \"ITC Franklin Gothic\", Arial, sans-serif", 259 | "font-size" : "12px", 260 | "font-weight": "normal", 261 | "font-style": "normal", 262 | "text-align" : "center", 263 | "color" : "#635F5D", 264 | "color_ref" : ["colors", "text", "main"] 265 | }, 266 | "axis_title" : 267 | { 268 | "font-family" : "\"Franklin Gothic Book\", \"Franklin Gothic Medium\", \"Franklin Gothic\", \"ITC Franklin Gothic\", Arial, sans-serif", 269 | "font-size" : "12px", 270 | "font-weight": "normal", 271 | "font-style": "normal", 272 | "text-align" : "center", 273 | "color" : "#635F5D", 274 | "color_ref" : ["colors", "text", "main"] 275 | }, 276 | "key_label" : 277 | { 278 | "font-family" : "\"Franklin Gothic Book\", \"Franklin Gothic Medium\", \"Franklin Gothic\", \"ITC Franklin Gothic\", Arial, sans-serif", 279 | "font-size" : "10px", 280 | "font-weight": "normal", 281 | "font-style": "normal", 282 | "text-align" : "left", 283 | "color" : "#635F5D", 284 | "color_ref" : ["colors", "text", "main"] 285 | }, 286 | "callout" : 287 | { 288 | "font-family": "font-family: Georgia, Times, \"Times New Roman\", serif", 289 | "font-size" : "10px", 290 | "font-weight": "normal", 291 | "font-style": "italic", 292 | "text-align" : "left", 293 | "color" : "#635F5D", 294 | "color_ref" : ["colors", "text", "main"] 295 | }, 296 | "point_label" : 297 | { 298 | "font-family" : "\"Franklin Gothic Book\", \"Franklin Gothic Medium\", \"Franklin Gothic\", \"ITC Franklin Gothic\", Arial, sans-serif", 299 | "font-size" : "10px", 300 | "font-weight": "normal", 301 | "font-style": "normal", 302 | "text-align" : "right", 303 | "color" : "#635F5D", 304 | "color_ref" : ["colors", "text", "main"] 305 | }, 306 | "citation_title" : 307 | { 308 | "font-family": "font-family: Georgia, Times, \"Times New Roman\", serif", 309 | "font-size" : "8px", 310 | "font-weight": "bold", 311 | "font-style": "normal", 312 | "text-align" : "left", 313 | "color" : "#8E8883", 314 | "color_ref" : ["colors", "text", "light"] 315 | }, 316 | "citation_source" : 317 | { 318 | "font-family": "font-family: Georgia, Times, \"Times New Roman\", serif", 319 | "font-size" : "10px", 320 | "font-weight": "normal", 321 | "font-style": "italic", 322 | "text-align" : "left", 323 | "color" : "#8E8883", 324 | "color_ref" : ["colors", "text", "light"] 325 | } 326 | }, 327 | 328 | "colors": 329 | { 330 | "background": 331 | { 332 | "hex" : "#EFECEA", 333 | "rgb" : [ 239, 236, 234 ], 334 | "cmyk": [ 5, 5, 5, 0 ], 335 | "default" : 336 | { 337 | "hex" : "#EFECEA", 338 | "rgb" : [ 239, 236, 234 ], 339 | "cmyk": [ 5, 5, 5, 0 ] 340 | }, 341 | "light": 342 | { 343 | "hex" : "#F5F3F2", 344 | "rgb" : [ 245, 243, 242 ], 345 | "cmyk": [ 2, 2, 2, 0 ] 346 | }, 347 | "dark": 348 | { 349 | "hex" : "#E5E2E0", 350 | "rgb" : [ 229, 226, 224 ], 351 | "cmyk": [ 9, 8, 8, 0 ] 352 | } 353 | }, 354 | "text": 355 | { 356 | "main" : 357 | { 358 | "hex" : "#635F5D", 359 | "rgb" : [ 99, 95, 93 ], 360 | "cmyk": [ 59, 54, 54, 25 ] 361 | }, 362 | "light" : 363 | { 364 | "hex" : "#8E8883", 365 | "rgb" : [ 142, 136, 131 ], 366 | "cmyk": [ 46, 41, 44, 5 ] 367 | } 368 | }, 369 | "line": 370 | { 371 | "white" : 372 | { 373 | "hex" : "#FFFFFF", 374 | "rgb" : [ 255, 255, 255 ], 375 | "cmyk": [ 0, 0, 0, 0 ] 376 | }, 377 | "grey" : 378 | { 379 | "hex" : "#C0C0BB", 380 | "rgb" : [ 192, 192, 187 ], 381 | "cmyk": [ 25, 19, 23, 0 ] 382 | } 383 | }, 384 | "data": 385 | { 386 | "main": 387 | { 388 | "yellow": 389 | { 390 | "hex" : "#E3BA22", 391 | "rgb" : [ 227, 186, 34 ], 392 | "cmyk": [ 12, 24, 100, 0 ], 393 | 394 | "default": 395 | { 396 | "hex" : "#E3BA22", 397 | "rgb" : [ 227, 186, 34 ], 398 | "cmyk": [ 12, 24, 100, 0 ] 399 | }, 400 | "subsets": 401 | [ 402 | { 403 | "hex" : "#F2DA57", 404 | "rgb" : [ 242, 218, 87 ], 405 | "cmyk": [ 6, 9, 78, 0 ] 406 | }, 407 | { 408 | "hex" : "#BD8F22", 409 | "rgb" : [ 242, 218, 87 ], 410 | "cmyk": null 411 | } 412 | ] 413 | }, 414 | 415 | "orange": 416 | { 417 | "hex" : "#E6842A", 418 | "rgb" : [ 230, 132, 42 ], 419 | "cmyk": [ 7, 57, 97, 1 ], 420 | 421 | "default": 422 | { 423 | "hex" : "#E6842A", 424 | "rgb" : [ 230, 132, 42 ], 425 | "cmyk": [ 7, 57, 97, 1 ] 426 | }, 427 | "subsets": 428 | [ 429 | { 430 | "hex" : "#F6B656", 431 | "rgb" : [ 246, 182, 86 ], 432 | "cmyk": [ 2, 31, 76, 0 ] 433 | }, 434 | { 435 | "hex" : "#BA5F06", 436 | "rgb" : [ 186, 95, 6 ], 437 | "cmyk": null 438 | } 439 | ] 440 | }, 441 | 442 | "cyan": 443 | { 444 | "hex" : "#137B80", 445 | "rgb" : [ 19, 123, 128 ], 446 | "cmyk": [ 86, 35, 46, 10 ], 447 | 448 | "default": 449 | { 450 | "hex" : "#137B80", 451 | "rgb" : [ 19, 123, 128 ], 452 | "cmyk": [ 86, 35, 46, 10 ] 453 | }, 454 | "subsets": 455 | [ 456 | { 457 | "hex" : "#42A5B3", 458 | "rgb" : [ 66, 165, 179 ], 459 | "cmyk": [ 70, 17, 28, 0 ] 460 | }, 461 | { 462 | "hex" : "#005D6E", 463 | "rgb" : [ 0, 93, 110 ], 464 | "cmyk": null 465 | } 466 | ] 467 | }, 468 | 469 | "magenta": 470 | { 471 | "hex" : "#8E6C8A", 472 | "rgb" : [ 142, 109, 138 ], 473 | "cmyk": [ 48, 61, 28, 4 ], 474 | 475 | "default": 476 | { 477 | "hex" : "#8E6C8A", 478 | "rgb" : [ 142, 109, 138 ], 479 | "cmyk": [ 48, 61, 28, 4 ] 480 | }, 481 | "subsets": 482 | [ 483 | { 484 | "hex" : "#B396AD", 485 | "rgb" : [ 179, 150, 173 ], 486 | "cmyk": [ 31, 42, 18, 0 ] 487 | }, 488 | { 489 | "hex" : "#684664", 490 | "rgb" : [ 104, 70, 100 ], 491 | "cmyk": null 492 | } 493 | ] 494 | }, 495 | 496 | "neutral": 497 | { 498 | "hex" : "#978F80", 499 | "rgb" : [ 151, 143, 128 ], 500 | "cmyk": [ 42, 38, 49, 4 ], 501 | 502 | "default": 503 | { 504 | "hex" : "#978F80", 505 | "rgb" : [ 151, 143, 128 ], 506 | "cmyk": [ 42, 38, 49, 4 ] 507 | }, 508 | "subsets": 509 | [ 510 | { 511 | "hex" : "#C1BAA9", 512 | "rgb" : [ 193, 186, 169 ], 513 | "cmyk": [ 25, 22, 32, 0 ] 514 | }, 515 | { 516 | "hex" : "#7C715E", 517 | "rgb" : [ 124, 113, 94 ], 518 | "cmyk": null 519 | } 520 | ] 521 | } 522 | 523 | }, 524 | "parties": 525 | { 526 | "republican": 527 | { 528 | "hex" : "#9A3E25", 529 | "rgb" : [ 154, 62, 37 ], 530 | "cmyk": [ 27, 84, 96, 22 ], 531 | 532 | "default": 533 | { 534 | "hex" : "#9A3E25", 535 | "rgb" : [ 154, 62, 37 ], 536 | "cmyk": [ 27, 84, 96, 22 ] 537 | }, 538 | "subsets": 539 | [ 540 | { 541 | "hex" : "#B37055", 542 | "rgb" : [ 179, 112, 85 ], 543 | "cmyk": [ 25, 60, 69, 8 ] 544 | } 545 | ] 546 | }, 547 | 548 | "democrat": 549 | { 550 | "hex" : "#156B90", 551 | "rgb" : [ 21, 107, 144 ], 552 | "cmyk": [ 89, 52, 27, 6 ], 553 | 554 | "default": 555 | { 556 | "hex" : "#156B90", 557 | "rgb" : [ 21, 107, 144 ], 558 | "cmyk": [ 89, 52, 27, 6 ] 559 | }, 560 | "subsets": 561 | [ 562 | { 563 | "hex" : "#688BAB", 564 | "rgb" : [ 104, 139, 171 ], 565 | "cmyk": [ 64, 38, 20, 1 ] 566 | } 567 | ] 568 | }, 569 | 570 | "independent": 571 | { 572 | "hex" : "#708259", 573 | "rgb" : [ 112, 130, 89 ], 574 | "cmyk": [ 58, 34, 73, 13 ], 575 | 576 | "default": 577 | { 578 | "hex" : "#708259", 579 | "rgb" : [ 112, 130, 89 ], 580 | "cmyk": [ 58, 34, 73, 13 ] 581 | }, 582 | "subsets": 583 | [ 584 | { 585 | "hex" : "#95A17E", 586 | "rgb" : [ 149, 161, 126 ], 587 | "cmyk": [ 44, 27, 57, 2 ] 588 | } 589 | ] 590 | } 591 | 592 | }, 593 | 594 | "pro_con": 595 | { 596 | "con": 597 | { 598 | "hex" : "#BD2D28", 599 | "rgb" : [ 189, 45, 40 ], 600 | "cmyk": [ 18, 95, 98, 8 ], 601 | 602 | "default": 603 | { 604 | "hex" : "#BD2D28", 605 | "rgb" : [ 189, 45, 40 ], 606 | "cmyk": [ 18, 95, 98, 8 ] 607 | }, 608 | "subsets": 609 | [ 610 | { 611 | "hex" : "#E25A42", 612 | "rgb" : [ 226, 90, 66 ], 613 | "cmyk": [ 6, 79, 80, 1 ] 614 | } 615 | ] 616 | }, 617 | 618 | "pro": 619 | { 620 | "hex" : "#0F8C79", 621 | "rgb" : [ 15, 140, 121 ], 622 | "cmyk": [ 84, 24, 60, 5 ], 623 | 624 | "default": 625 | { 626 | "hex" : "#0F8C79", 627 | "rgb" : [ 15, 140, 121 ], 628 | "cmyk": [ 84, 24, 60, 5 ] 629 | }, 630 | "subsets": 631 | [ 632 | { 633 | "hex" : "#6BBBA1", 634 | "rgb" : [ 107, 187, 161 ], 635 | "cmyk": [ 58, 5, 45, 0 ] 636 | } 637 | ] 638 | } 639 | }, 640 | 641 | "money": 642 | { 643 | "positive": 644 | { 645 | "hex" : "#5C8100", 646 | "rgb" : [ 92, 129, 0 ], 647 | "cmyk": [ 67, 30, 100, 13 ], 648 | 649 | "default": 650 | { 651 | "hex" : "#5C8100", 652 | "rgb" : [ 92, 129, 0 ], 653 | "cmyk": [ 67, 30, 100, 13 ] 654 | }, 655 | "subsets": 656 | [ 657 | { 658 | "hex" : "#A0B700", 659 | "rgb" : [ 160, 183, 0 ], 660 | "cmyk": [ 43, 12, 100, 0 ] 661 | } 662 | ] 663 | }, 664 | 665 | "negative": 666 | { 667 | "hex" : null, 668 | "rgb" : null, 669 | "cmyk": null, 670 | 671 | "default": 672 | { 673 | "hex" : null, 674 | "rgb" : null, 675 | "cmyk": null 676 | }, 677 | "subsets": 678 | [ 679 | { 680 | "hex" : null, 681 | "rgb" : null, 682 | "cmyk": null 683 | } 684 | ] 685 | } 686 | } 687 | }, 688 | 689 | "choropleth": 690 | { 691 | "sequential": 692 | { 693 | "republican": 694 | { 695 | "no_data": 696 | { 697 | "hex" : "#E5E2E0", 698 | "rgb" : [ 229, 226, 224 ], 699 | "cmyk": [ 9, 8, 8, 0 ] 700 | }, 701 | "scale": 702 | [ 703 | { 704 | "hex" : "#712422", 705 | "rgb" : [ null, null, null ], 706 | "cmyk": [ null, null, null, null ] 707 | }, 708 | { 709 | "hex" : "#9A3E25", 710 | "rgb" : [ null, null, null ], 711 | "cmyk": [ null, null, null, null ] 712 | }, 713 | { 714 | "hex" : "#B37055", 715 | "rgb" : [ null, null, null ], 716 | "cmyk": [ null, null, null, null ] 717 | }, 718 | { 719 | "hex" : "#D9A78D", 720 | "rgb" : [ null, null, null ], 721 | "cmyk": [ null, null, null, null ] 722 | }, 723 | { 724 | "hex" : "#EDCEBC", 725 | "rgb" : [ null, null, null ], 726 | "cmyk": [ null, null, null, null ] 727 | } 728 | ] 729 | }, 730 | "democrat": 731 | { 732 | "no_data": 733 | { 734 | "hex" : "#E5E2E0", 735 | "rgb" : [ 229, 226, 224 ], 736 | "cmyk": [ 9, 8, 8, 0] 737 | }, 738 | "scale": 739 | [ 740 | { 741 | "hex" : "#05426C", 742 | "rgb" : [ null, null, null ], 743 | "cmyk": [ null, null, null, null ] 744 | }, 745 | { 746 | "hex" : "#E6842A", 747 | "rgb" : [ null, null, null ], 748 | "cmyk": [ null, null, null, null ] 749 | }, 750 | { 751 | "hex" : "#688BAB", 752 | "rgb" : [ null, null, null ], 753 | "cmyk": [ null, null, null, null ] 754 | }, 755 | { 756 | "hex" : "#8CAEC6", 757 | "rgb" : [ null, null, null ], 758 | "cmyk": [ null, null, null, null ] 759 | }, 760 | { 761 | "hex" : "#BAD2E2", 762 | "rgb" : [ null, null, null ], 763 | "cmyk": [ null, null, null, null ] 764 | } 765 | ] 766 | }, 767 | "money": 768 | { 769 | "no_data": 770 | { 771 | "hex" : "#E5E2E0", 772 | "rgb" : [ 229, 226, 224 ], 773 | "cmyk": [ 9, 8, 8, 0] 774 | }, 775 | "scale": 776 | [ 777 | { 778 | "hex" : "#0C4E00", 779 | "rgb" : [ null, null, null ], 780 | "cmyk": [ null, null, null, null ] 781 | }, 782 | { 783 | "hex" : "#5C8100", 784 | "rgb" : [ null, null, null ], 785 | "cmyk": [ null, null, null, null ] 786 | }, 787 | { 788 | "hex" : "#A0B700", 789 | "rgb" : [ null, null, null ], 790 | "cmyk": [ null, null, null, null ] 791 | }, 792 | { 793 | "hex" : "#D2CF00", 794 | "rgb" : [ null, null, null ], 795 | "cmyk": [ null, null, null, null ] 796 | }, 797 | { 798 | "hex" : "#E6E4A6", 799 | "rgb" : [ null, null, null ], 800 | "cmyk": [ null, null, null, null ] 801 | } 802 | ] 803 | }, 804 | "generic": 805 | { 806 | "no_data": 807 | { 808 | "hex" : "#E5E2E0", 809 | "rgb" : [ 229, 226, 224 ], 810 | "cmyk": [ 9, 8, 8, 0] 811 | }, 812 | "scale": 813 | [ 814 | { 815 | "hex" : "#936B00", 816 | "rgb" : [ null, null, null ], 817 | "cmyk": [ null, null, null, null ] 818 | }, 819 | { 820 | "hex" : "#BD8F22", 821 | "rgb" : [ null, null, null ], 822 | "cmyk": [ null, null, null, null ] 823 | }, 824 | { 825 | "hex" : "#E3BA22", 826 | "rgb" : [ null, null, null ], 827 | "cmyk": [ null, null, null, null ] 828 | }, 829 | { 830 | "hex" : "#F2DA57", 831 | "rgb" : [ null, null, null ], 832 | "cmyk": [ null, null, null, null ] 833 | }, 834 | { 835 | "hex" : "F1E8AE", 836 | "rgb" : [ null, null, null ], 837 | "cmyk": [ null, null, null, null ] 838 | } 839 | ] 840 | } 841 | }, 842 | "diverging": 843 | { 844 | "republican_democrat": 845 | { 846 | "no_data": 847 | { 848 | "hex" : "#E5E2E0", 849 | "rgb" : [ 229, 226, 224 ], 850 | "cmyk": [ 9, 8, 8, 0] 851 | }, 852 | "scale": 853 | [ 854 | { 855 | "hex" : "#9A3E25", 856 | "rgb" : [ null, null, null ], 857 | "cmyk": [ null, null, null, null ] 858 | }, 859 | { 860 | "hex" : "#D9A78D", 861 | "rgb" : [ null, null, null ], 862 | "cmyk": [ null, null, null, null ] 863 | }, 864 | { 865 | "hex" : "#C8C6C6", 866 | "rgb" : [ null, null, null ], 867 | "cmyk": [ null, null, null, null ] 868 | }, 869 | { 870 | "hex" : "#8CAEC6", 871 | "rgb" : [ null, null, null ], 872 | "cmyk": [ null, null, null, null ] 873 | }, 874 | { 875 | "hex" : "#E6842A", 876 | "rgb" : [ null, null, null ], 877 | "cmyk": [ null, null, null, null ] 878 | } 879 | ] 880 | }, 881 | "anti_pro": 882 | { 883 | "no_data": 884 | { 885 | "hex" : "#E5E2E0", 886 | "rgb" : [ 229, 226, 224 ], 887 | "cmyk": [ 9, 8, 8, 0] 888 | }, 889 | "scale": 890 | [ 891 | { 892 | "hex" : "#BD2D28", 893 | "rgb" : [ null, null, null ], 894 | "cmyk": [ null, null, null, null ] 895 | }, 896 | { 897 | "hex" : "#C37A73", 898 | "rgb" : [ null, null, null ], 899 | "cmyk": [ null, null, null, null ] 900 | }, 901 | { 902 | "hex" : "#C8C6C6", 903 | "rgb" : [ null, null, null ], 904 | "cmyk": [ null, null, null, null ] 905 | }, 906 | { 907 | "hex" : "#75A999", 908 | "rgb" : [ null, null, null ], 909 | "cmyk": [ null, null, null, null ] 910 | }, 911 | { 912 | "hex" : "#0F8C79", 913 | "rgb" : [ null, null, null ], 914 | "cmyk": [ null, null, null, null ] 915 | } 916 | ] 917 | } 918 | } 919 | }, 920 | 921 | "network_graph": 922 | { 923 | "yellows": 924 | [ 925 | { 926 | "hex" : "#F2DA57", 927 | "rgb" : [ 242, 218, 87 ], 928 | "cmyk": [ null, null, null, null ] 929 | }, 930 | { 931 | "hex" : "#E2BA22", 932 | "rgb" : [ 227, 186, 34 ], 933 | "cmyk": [ null, null, null, null ] 934 | }, 935 | { 936 | "hex" : "#B08B12", 937 | "rgb" : [ 176, 139, 18 ], 938 | "cmyk": [ null, null, null, null ] 939 | } 940 | ], 941 | "oranges": 942 | [ 943 | { 944 | "hex" : "#F6B656", 945 | "rgb" : [ 244, 182, 86 ], 946 | "cmyk": [ null, null, null, null ] 947 | }, 948 | { 949 | "hex" : "#E58429", 950 | "rgb" : [ 229, 132, 41 ], 951 | "cmyk": [ null, null, null, null ] 952 | }, 953 | { 954 | "hex" : "#BA5F06", 955 | "rgb" : [ 186, 95, 6 ], 956 | "cmyk": [ null, null, null, null ] 957 | } 958 | ], 959 | "reds": 960 | [ 961 | { 962 | "hex" : "#E25A42", 963 | "rgb" : [ 226, 90, 66 ], 964 | "cmyk": [ null, null, null, null ] 965 | }, 966 | { 967 | "hex" : "#BD2D28", 968 | "rgb" : [ 189, 45, 40 ], 969 | "cmyk": [ null, null, null, null ] 970 | }, 971 | { 972 | "hex" : "#8C3B00", 973 | "rgb" : [ 140, 59, 0 ], 974 | "cmyk": [ null, null, null, null ] 975 | } 976 | ], 977 | "pinks": 978 | [ 979 | { 980 | "hex" : "#DCBDCF", 981 | "rgb" : [ 220, 189, 207 ], 982 | "cmyk": [ null, null, null, null ] 983 | }, 984 | { 985 | "hex" : "#D15A86", 986 | "rgb" : [ 209, 90, 134 ], 987 | "cmyk": [ null, null, null, null ] 988 | }, 989 | { 990 | "hex" : "#6D191B", 991 | "rgb" : [ 109, 25, 27 ], 992 | "cmyk": [ null, null, null, null ] 993 | } 994 | ], 995 | "magentas": 996 | [ 997 | { 998 | "hex" : "#B396AD", 999 | "rgb" : [ 179, 150, 173 ], 1000 | "cmyk": [ null, null, null, null ] 1001 | }, 1002 | { 1003 | "hex" : "#8E6C8A", 1004 | "rgb" : [ 142, 108, 138 ], 1005 | "cmyk": [ null, null, null, null ] 1006 | }, 1007 | { 1008 | "hex" : "#842854", 1009 | "rgb" : [ 132, 40, 84 ], 1010 | "cmyk": [ null, null, null, null ] 1011 | } 1012 | ], 1013 | "blues": 1014 | [ 1015 | { 1016 | "hex" : "#B0CBDB", 1017 | "rgb" : [ 176, 203, 219 ], 1018 | "cmyk": [ null, null, null, null ] 1019 | }, 1020 | { 1021 | "hex" : "#6B99A1", 1022 | "rgb" : [ 107, 153, 161 ], 1023 | "cmyk": [ null, null, null, null ] 1024 | }, 1025 | { 1026 | "hex" : "#5F7186", 1027 | "rgb" : [ 95, 113, 134 ], 1028 | "cmyk": [ null, null, null, null ] 1029 | } 1030 | ], 1031 | "cyans": 1032 | [ 1033 | { 1034 | "hex" : "#33B6D0", 1035 | "rgb" : [ 51, 182, 208 ], 1036 | "cmyk": [ null, null, null, null ] 1037 | }, 1038 | { 1039 | "hex" : "#42A5B3", 1040 | "rgb" : [ 66, 165, 179 ], 1041 | "cmyk": [ null, null, null, null ] 1042 | }, 1043 | { 1044 | "hex" : "#5F7186", 1045 | "rgb" : [ 25, 53, 86 ], 1046 | "cmyk": [ null, null, null, null ] 1047 | } 1048 | ], 1049 | "teals": 1050 | [ 1051 | { 1052 | "hex" : "#7ABFCC", 1053 | "rgb" : [ 122, 191, 204 ], 1054 | "cmyk": [ null, null, null, null ] 1055 | }, 1056 | { 1057 | "hex" : "#0F8C79", 1058 | "rgb" : [ 15, 140, 121 ], 1059 | "cmyk": [ null, null, null, null ] 1060 | }, 1061 | { 1062 | "hex" : "#137B80", 1063 | "rgb" : [ 19, 123, 128 ], 1064 | "cmyk": [ null, null, null, null ] 1065 | } 1066 | ], 1067 | "mints": 1068 | [ 1069 | { 1070 | "hex" : "#C8D7A1", 1071 | "rgb" : [ 200, 215, 161 ], 1072 | "cmyk": [ null, null, null, null ] 1073 | }, 1074 | { 1075 | "hex" : "#6BBBA1", 1076 | "rgb" : [ 107, 187, 161 ], 1077 | "cmyk": [ null, null, null, null ] 1078 | }, 1079 | { 1080 | "hex" : "#144847", 1081 | "rgb" : [ 20, 72, 71 ], 1082 | "cmyk": [ null, null, null, null ] 1083 | } 1084 | ], 1085 | "greens": 1086 | [ 1087 | { 1088 | "hex" : "#A0B700", 1089 | "rgb" : [ 160, 183, 0 ], 1090 | "cmyk": [ null, null, null, null ] 1091 | }, 1092 | { 1093 | "hex" : "#5C8100", 1094 | "rgb" : [ 92, 129, 0 ], 1095 | "cmyk": [ null, null, null, null ] 1096 | }, 1097 | { 1098 | "hex" : "#254E00", 1099 | "rgb" : [ 37, 78, 0 ], 1100 | "cmyk": [ null, null, null, null ] 1101 | } 1102 | ] 1103 | } 1104 | } 1105 | } 1106 | --------------------------------------------------------------------------------