├── .gitignore ├── MIT-LICENSE ├── README.md ├── Rakefile ├── _site ├── audio │ ├── alert-green.mp3 │ ├── alert-red.mp3 │ └── alert-yellow.mp3 ├── css │ ├── app.css │ └── libs │ │ ├── cubism.css │ │ └── style.css ├── img │ ├── apple-touch-icon.png │ ├── glyphicons-halflings-white.png │ ├── glyphicons-halflings.png │ └── spinner.gif ├── index.html └── js │ ├── app.js │ ├── cubism.js │ └── libs │ ├── colorbrewer.min.js │ ├── cubism.elasticsearch.js │ ├── cubism.v1.js │ ├── d3.v2.min.js │ ├── ember-0.9.8.js │ ├── ember-0.9.8.min.js │ └── jquery-1.7.2.min.js ├── elasticsearch-paramedic-screenshot.png └── plugin-descriptor.properties /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | tmp/ 3 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2012 Karel Minarik 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ElasticSearch Paramedic 2 | ======================= 3 | 4 | Paramedic is a simple yet sexy tool to monitor and inspect [ElasticSearch](http://elasticsearch.org) clusters. 5 | 6 | It displays real-time statistics and information about your nodes and indices, 7 | as well as shard allocation within the cluster. 8 | 9 | The application is written in JavaScript, using the [Ember.js](http://emberjs.com/) framework for sanity 10 | and the [Cubism.js](http://square.github.com/cubism/) library for visuals. While the project is 11 | _useful_, the codebase, with most logic in controllers, lacking proper component separation and test suite, 12 | can't be considered mature enough, yet. 13 | 14 | For basic overview, see a screenshot below. 15 | 16 | ![ElasticSearch Paramedic Screenshot](/elasticsearch-paramedic-screenshot.png) 17 | 18 | Versions 19 | -------- 20 | 21 | | elasticsearch version | branch | latest version | 22 | | --------------------- | ------ | --------------- | 23 | | 1.X | 1.0 | 1.0 | 24 | | 2.X | 2.0 | 2.0 | 25 | 26 | Installation 27 | ------------ 28 | 29 | The easiest way to check out the application is to open it in a modern browser: 30 | ****. 31 | 32 | If you have ElasticSearch running on `http://localhost:9200`, you should see the stats for your cluster. 33 | 34 | You can also download or clone this repository and open the `index.html` file in your browser: 35 | 36 | git clone git://github.com/karmi/elasticsearch-paramedic.git && cd elasticsearch-paramedic 37 | cd elasticsearch-paramedic 38 | git checkout {branch|version} 39 | open index.html 40 | 41 | The easiest way to use Paramedic in production is to install it as an ElasticSearch plugin: 42 | 43 | bin/plugin install karmi/elasticsearch-paramedic/{branch|version} 44 | 45 | If your cluster is publicly accessible (authenticated with firewall rules or HTTP Authentication via proxy), 46 | open it in your browser: 47 | 48 | open http://localhost:9200/_plugin/paramedic/index.html 49 | 50 | 51 | Overview 52 | -------- 53 | 54 | The application displays basic information about your cluster: cluster name, health, number of nodes and shards, 55 | etc., using the [Cluster Health](http://www.elasticsearch.org/guide/reference/api/admin-cluster-health.html) API. 56 | 57 | The “Stats” chart displays key metrics from the 58 | [Nodes Stats](http://www.elasticsearch.org/guide/reference/api/admin-cluster-nodes-stats.html) API, 59 | updated every second. 60 | 61 | The “Nodes” part displays the most important information about the cluster nodes (used disk space and memory, 62 | number of nodes, machine load and ElasticSearch CPU consumption, etc.), using the 63 | [Nodes Info](http://www.elasticsearch.org/guide/reference/api/admin-cluster-nodes-info.html) and 64 | [Nodes Stats](http://www.elasticsearch.org/guide/reference/api/admin-cluster-nodes-stats.html) APIs. 65 | 66 | The “Indices” part displays basic information about the indices: number of primary shards, number of replicas, 67 | basic index statistics, using the 68 | [Cluster State](http://www.elasticsearch.org/guide/reference/api/admin-cluster-state.html), 69 | [Indices Status](http://www.elasticsearch.org/guide/reference/api/admin-indices-status.html) and 70 | [Indices Stats](http://www.elasticsearch.org/guide/reference/api/admin-indices-stats.html) APIs. 71 | Primary shards are displayed in _blue_, allocated replicas in _green_, unassigned replicas in _yellow_, 72 | and unassigned (missing) primary shards in _red_. 73 | 74 | To display shard allocation across the nodes, use the “Show Details” button. All information is updated periodically, 75 | which allows you to see node and index statistics, shard initialization or relocation, etc. in real time. 76 | 77 | Note, that a considerable number of Ajax calls is being performed, and launching the application 78 | for large clusters, with large number of nodes and indices/shards, may leave your 79 | browser unresponsive, or crash your machine. Try increasing the polling interval and hiding the charts 80 | if you experience performance problems. 81 | 82 | The application performance has been successfuly tested for clusters with around five nodes and sixty shards. 83 | 84 | 85 | Similar Applications 86 | -------------------- 87 | 88 | You are encouraged to try similar existing tools for ElasticSearch: 89 | 90 | * [BigDesk](http://github.com/lukas-vlcek/bigdesk) 91 | * [elasticsearch-head](http://github.com/mobz/elasticsearch-head) 92 | * [Sematext SPM](http://sematext.com/spm) 93 | * [Munin Plugins](https://gist.github.com/2159398) 94 | 95 | ----- 96 | 97 | [Karel Minarik](http://karmi.cz) 98 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | STDOUT.sync = true 2 | 3 | require 'rake' 4 | require 'tire' 5 | 6 | namespace :index do 7 | 8 | COUNT = (ENV['COUNT'] || 10).to_i 9 | LEAVE = (ENV['LEAVE'] || 2 ).to_i 10 | 11 | desc "Create indices (pass COUNT in ENV)" 12 | task :create do 13 | 1.upto(COUNT) do |i| 14 | print "#{i}. " 15 | Timeout::timeout(0.25) do 16 | Tire.index("index_#{i}") do 17 | create settings: { number_of_shards: 3, number_of_replicas: 1 } 18 | store title: "Test doc" 19 | end 20 | end rescue Timeout::Error 21 | end 22 | end 23 | 24 | desc "Remove indices (pass LEAVE in ENV)" 25 | task :remove do 26 | COUNT.downto(LEAVE+1) do |i| 27 | print "#{i}. " 28 | Tire.index("index_#{i}").delete 29 | end 30 | end 31 | 32 | desc "Search an index in a loop" 33 | task :search do 34 | trap(:INT) { exit } 35 | INDEX = ENV['INDEX'] || 'index_1' 36 | loop do 37 | Tire.search(INDEX) { query { string 'tes*' } }.results 38 | print '.' 39 | sleep 0.2 40 | end 41 | end 42 | 43 | end 44 | -------------------------------------------------------------------------------- /_site/audio/alert-green.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karmi/elasticsearch-paramedic/0c8df1654d6574a6509c9599a63413dab046305b/_site/audio/alert-green.mp3 -------------------------------------------------------------------------------- /_site/audio/alert-red.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karmi/elasticsearch-paramedic/0c8df1654d6574a6509c9599a63413dab046305b/_site/audio/alert-red.mp3 -------------------------------------------------------------------------------- /_site/audio/alert-yellow.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karmi/elasticsearch-paramedic/0c8df1654d6574a6509c9599a63413dab046305b/_site/audio/alert-yellow.mp3 -------------------------------------------------------------------------------- /_site/css/app.css: -------------------------------------------------------------------------------- 1 | /* Common */ 2 | 3 | body 4 | { font-family: 'Helvetica Neue', Helvetica, sans-serif; 5 | margin: 2em; } 6 | 7 | h2.label 8 | { color: #b2b2b2; font-size: 12px; font-weight: 300; text-transform: uppercase; border-bottom: 1px solid #e0e0e0; } 9 | 10 | span.label 11 | { color: #b2b2b2; font-size: 12px; font-weight: 300; text-transform: uppercase; padding: 0; margin: 0; } 12 | 13 | .dimmed, .dimmed * 14 | { color: #b2b2b2; } 15 | 16 | .loading 17 | { color: #999; 18 | font-size: 90%; 19 | background: transparent url('../img/spinner.gif') no-repeat 0 50%; 20 | padding: 0 0 0 21px; } 21 | 22 | /* Header */ 23 | header 24 | { margin: 0 0 1.5em 0; 25 | padding: 0; } 26 | header h1 27 | { font-size: 28px; 28 | padding: 0; 29 | margin: -0.25em 0 0.1em 0; } 30 | 31 | header section 32 | { width: 15em; 33 | float: left; } 34 | 35 | header section.cluster_name 36 | { width: 40em; } 37 | header section.endpoint 38 | { width: 30em; } 39 | 40 | header section #elasticsearch_url 41 | { font: bolder 13px 'Helvetica Neue', Helvetica, sans-serif; 42 | border: none; 43 | border-bottom: 1px dotted #ccc; 44 | width: 30em;} 45 | 46 | header section p.refresh 47 | { display: inline-block; } 48 | 49 | header section p.refresh .refresh-label 50 | { color: #999; 51 | margin-top: 0.5em; 52 | display: inline-block; } 53 | header section p.refresh:hover .refresh-label 54 | { color: #666; } 55 | 56 | header section p.refresh .refresh-controls 57 | { color: #999; 58 | position: relative; 59 | top: -0.15em; 60 | display: inline-block; } 61 | header section p.refresh .refresh-controls button, 62 | header section p.refresh .refresh-controls select 63 | { color: #999; 64 | -webkit-text-fill-color: #999; } 65 | header section p.refresh:hover .refresh-controls button, 66 | header section p.refresh:hover .refresh-controls select 67 | { color: #333; 68 | -webkit-text-fill-color: #333; } 69 | 70 | header section p.alerts 71 | { margin: 0 0 0 1em; 72 | display: inline-block; } 73 | 74 | header section p.alerts input 75 | { position: relative; 76 | top: -0.2em; } 77 | 78 | header section p.alerts label 79 | { color: #d0d0d0; 80 | font-size: 90%; } 81 | 82 | header section.endpoint .icon-refresh 83 | { position: relative; 84 | width: 14px; 85 | height: 14px; 86 | opacity: 0.2; } 87 | 88 | header section.endpoint.polling-active .icon-refresh 89 | { opacity: 0.4; } 90 | 91 | header section.endpoint.polling-in-progress .icon-refresh 92 | { opacity: 0.5; 93 | -webkit-animation-name: rotateRefreshIcon; 94 | -webkit-animation-duration:1s; 95 | -webkit-animation-iteration-count:infinite; 96 | -webkit-animation-timing-function:linear; } 97 | 98 | @-webkit-keyframes rotateRefreshIcon 99 | { from {-webkit-transform: rotate(0deg);} 100 | to {-webkit-transform: rotate(360deg);} } 101 | 102 | header section .status 103 | { background: #f5f5f5; 104 | font-weight: bolder; 105 | font-size: 12px; 106 | letter-spacing: -1px; 107 | text-transform: uppercase; 108 | padding: 0.25em 0.5em 0.25em 0.45em; 109 | border-radius: 0.25em; } 110 | header section .status.green 111 | { color: #fff; 112 | background: #97f218; } 113 | header section .status.yellow 114 | { color: #fff; 115 | background: #f2bc18; } 116 | header section .status.red 117 | { color: #fff; 118 | background: #f2184f; } 119 | 120 | header section .label.darker 121 | { color: #333; } 122 | 123 | /* Nodes */ 124 | 125 | #nodes-wrapper h2 a 126 | { color: #999; 127 | text-transform: none; 128 | float: right; } 129 | 130 | #nodes-wrapper h2 a:hover 131 | { color: #666; 132 | border-bottom: 1px solid #666; 133 | cursor: pointer; } 134 | 135 | #nodes .node 136 | { margin: 0.25em 0.5em 0.25em 0; 137 | padding: 0.25em 0.5em 0.25em 0; 138 | float: left; } 139 | 140 | #nodes .node h3 141 | { color: #444; 142 | font-size: 16px; } 143 | 144 | #nodes .node .meta 145 | { color: #333; 146 | border-right: 1px solid #f5f5f5; 147 | padding-right: 0.5em; } 148 | 149 | /* Indices */ 150 | 151 | #indices-wrapper h2 a 152 | { color: #999; 153 | text-transform: none; 154 | float: right; } 155 | 156 | #indices-wrapper h2 a:hover 157 | { color: #666; 158 | border-bottom: 1px solid #666; 159 | cursor: pointer; } 160 | 161 | #indices 162 | { counter-reset: index; } 163 | 164 | #indices .index 165 | { border-bottom: 1px solid #999; 166 | clear: both; } 167 | 168 | #indices .index.close, 169 | #indices .index.close a 170 | { color: #999 !important; } 171 | 172 | #indices .index .basic-info 173 | { padding: 0.5em 0 0.5em 0; } 174 | 175 | #indices .index:hover .basic-info 176 | { background-color: #fafafa; } 177 | 178 | #indices .index .basic-info h3 179 | { min-width: 15em; 180 | counter-increment: index; } 181 | #indices .index .basic-info h3:before 182 | { content: counter(index) ". "; 183 | color: #999; 184 | font-weight: normal; 185 | font-size: 11px; } 186 | 187 | #indices .index:hover .basic-info h3, 188 | #indices .index.expanded .basic-info h3 189 | { text-shadow: #fff 0px 1px 0px; } 190 | 191 | #indices .index .basic-info h3 a 192 | { color: #444; 193 | text-decoration: none; } 194 | #indices .index:hover .basic-info h3 a 195 | { text-decoration: underline; } 196 | 197 | #indices .index .basic-info .buttons 198 | { margin-right: 1em; 199 | visibility: hidden; } 200 | #indices .index:hover .basic-info .buttons, 201 | #indices .index.expanded .basic-info.buttons 202 | { visibility: visible; } 203 | 204 | 205 | #indices .index .basic-info .meta 206 | { color: #999; 207 | float: right; } 208 | #indices .index.close .basic-info .meta 209 | { color: #999; } 210 | #indices .index:hover .basic-info .meta 211 | { color: #666; } 212 | #indices .index.close:hover .basic-info .meta 213 | { color: #999; } 214 | 215 | #indices .index h3 216 | { font-size: 14px; 217 | margin-right: 0.5em; 218 | float: left; } 219 | 220 | #indices .index h3 small 221 | { font-weight: normal; } 222 | 223 | #indices .index .shards 224 | { float: left; } 225 | 226 | #indices .index .shards .shard 227 | { margin: -0.25em 0.25em 0 0; } 228 | 229 | #indices .index .buttons 230 | { float: left; } 231 | 232 | /* Index extra info */ 233 | 234 | #indices .index .extra-info 235 | { /*display: none;*/ 236 | border-top: 1px solid #e0e0e0; 237 | width: 100%; } 238 | 239 | #indices .index .extra-info .loading 240 | { margin: 0.5em 0 0.5em 0; } 241 | 242 | /* Shards */ 243 | 244 | .index .shards .shard 245 | { background: #ccc; 246 | border-radius: 3px; 247 | float: left; 248 | padding: 0.25em; 249 | margin: 0.25em 0.25em 0.25em 0; } 250 | 251 | .index .shard.primary 252 | { background: #62b9f3; } 253 | 254 | .index .shard.STARTED 255 | { background: #bbf866; } 256 | .index .shard.UNASSIGNED 257 | { background: #ffea3f; } 258 | 259 | .index .shard.primary.STARTED 260 | { background: #62b9f3; } 261 | .index .shard.primary.UNASSIGNED 262 | { color: #fff; 263 | background: #d72e59; } 264 | 265 | .index .shard h3 266 | { color: #444 !important; } 267 | 268 | /* Node Shard Allocation */ 269 | 270 | #indices .extra-info .node 271 | { background: #f9f9f9; 272 | border-radius: 5px; 273 | padding: 0.5em; 274 | margin: 0.45em 0.25em 0.45em 0; 275 | float: left; } 276 | 277 | #indices .extra-info .node h3 278 | { color: #666; 279 | font-size: 14px; 280 | margin-bottom: 0.45em; 281 | float: none; 282 | clear: both; } 283 | #indices .extra-info .node h3 small 284 | { color: #999; } 285 | 286 | #indices .extra-info .node .shard 287 | { border-radius: 5px; 288 | margin-bottom: 0.5em !important; 289 | width: 11em; 290 | height: 4em; } 291 | 292 | #indices .extra-info .node .shard h3 293 | { font-size: 42px; 294 | margin: 0; 295 | float: left; } 296 | #indices .extra-info .node .shard .meta 297 | { margin: 0.75em 0 0 2.25em; } 298 | 299 | /* Cubism */ 300 | 301 | #cubism 302 | { font-weight: 300; 303 | margin: 0 0 1em 0; } 304 | #cubism h2 a 305 | { color: #999; 306 | text-transform: none; 307 | float: right; } 308 | #cubism h2 a:hover 309 | { color: #666; 310 | border-bottom: 1px solid #666; 311 | cursor: pointer; } 312 | #cubism .horizon:hover 313 | { background-color: #fafafa; } 314 | #cubism .horizon:hover .title 315 | { opacity: 0.99; } 316 | #cubism:hover .axis text { fill: #333; } 317 | #cubism .axis { border-color: #999 !important; } 318 | #cubism .axis path, #cubism .axis line { stroke: #999; } 319 | #cubism .axis text { fill: #999; } 320 | #cubism .horizon 321 | { border-color: #999; } 322 | #cubism .horizon .title, 323 | #cubism .horizon .value 324 | { font-size: 12px; 325 | top: -2px; 326 | text-shadow: rgba(255, 255, 255, 0.9) 0.1em 0.1em 0.09em !important; } 327 | #cubism .horizon .title 328 | { font-weight: normal; 329 | opacity: 0.9; 330 | margin-left: 0; } 331 | #cubism .horizon .value 332 | { font-weight: bolder; } 333 | -------------------------------------------------------------------------------- /_site/css/libs/cubism.css: -------------------------------------------------------------------------------- 1 | svg 2 | { font: 10px sans-serif; } 3 | 4 | .axis 5 | { font: 10px sans-serif; } 6 | 7 | .axis.top 8 | { border-bottom: solid 1px #333; } 9 | 10 | .axis path, .axis line 11 | { fill: none; 12 | stroke: #000; 13 | shape-rendering: crispEdges; } 14 | 15 | .axis text 16 | { -webkit-transition: fill-opacity 250ms linear; } 17 | 18 | .axis path 19 | { display: none; } 20 | 21 | .axis line 22 | { stroke: #000; 23 | shape-rendering: crispEdges; } 24 | 25 | .horizon 26 | { border-bottom: solid 1px #000; 27 | overflow: hidden; 28 | position: relative; } 29 | 30 | .horizon 31 | { border-bottom: solid 1px #000; } 32 | 33 | .horizon + .horizon 34 | { border-top: none; } 35 | 36 | .horizon canvas 37 | { display: block; } 38 | 39 | .horizon .title, 40 | .horizon .value 41 | { bottom: 0; 42 | line-height: 30px; 43 | margin: 0 6px; 44 | position: absolute; 45 | text-shadow: 0 1px 0 rgba(255,255,255,.5); 46 | white-space: nowrap; } 47 | 48 | .horizon .title 49 | { left: 0; } 50 | 51 | .horizon .value 52 | { right: 0; } 53 | 54 | .line 55 | { background: #000; 56 | opacity: .2; 57 | z-index: 2; } 58 | -------------------------------------------------------------------------------- /_site/css/libs/style.css: -------------------------------------------------------------------------------- 1 | 2 | /* ==== Scroll down to find where to put your styles :) ==== */ 3 | 4 | /* HTML5 ✰ Boilerplate */ 5 | 6 | html, body, div, span, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | abbr, address, cite, code, del, dfn, em, img, ins, kbd, q, samp, 9 | small, strong, sub, sup, var, b, i, dl, dt, dd, ol, ul, li, 10 | fieldset, form, label, legend, 11 | table, caption, tbody, tfoot, thead, tr, th, td, 12 | article, aside, canvas, details, figcaption, figure, 13 | footer, header, hgroup, menu, nav, section, summary, 14 | time, mark, audio, video { 15 | margin: 0; 16 | padding: 0; 17 | border: 0; 18 | font-size: 100%; 19 | font: inherit; 20 | vertical-align: baseline; 21 | } 22 | 23 | article, aside, details, figcaption, figure, 24 | footer, header, hgroup, menu, nav, section { 25 | display: block; 26 | } 27 | 28 | blockquote, q { quotes: none; } 29 | blockquote:before, blockquote:after, 30 | q:before, q:after { content: ''; content: none; } 31 | ins { background-color: #ff9; color: #000; text-decoration: none; } 32 | mark { background-color: #ff9; color: #000; font-style: italic; font-weight: bold; } 33 | del { text-decoration: line-through; } 34 | abbr[title], dfn[title] { border-bottom: 1px dotted; cursor: help; } 35 | table { border-collapse: collapse; border-spacing: 0; } 36 | hr { display: block; height: 1px; border: 0; border-top: 1px solid #ccc; margin: 1em 0; padding: 0; } 37 | input, select { vertical-align: middle; } 38 | 39 | body { font:13px/1.231 sans-serif; *font-size:small; } 40 | select, input, textarea, button { font:99% sans-serif; } 41 | pre, code, kbd, samp { font-family: monospace, sans-serif; } 42 | 43 | html { overflow-y: scroll; } 44 | a:hover, a:active { outline: none; } 45 | ul, ol { margin-left: 2em; } 46 | ol { list-style-type: decimal; } 47 | nav ul, nav li { margin: 0; list-style:none; list-style-image: none; } 48 | small { font-size: 85%; } 49 | strong, th { font-weight: bold; } 50 | td { vertical-align: top; } 51 | 52 | sub, sup { font-size: 75%; line-height: 0; position: relative; } 53 | sup { top: -0.5em; } 54 | sub { bottom: -0.25em; } 55 | 56 | pre { white-space: pre; white-space: pre-wrap; word-wrap: break-word; padding: 15px; } 57 | textarea { overflow: auto; } 58 | .ie6 legend, .ie7 legend { margin-left: -7px; } 59 | input[type="radio"] { vertical-align: text-bottom; } 60 | input[type="checkbox"] { vertical-align: bottom; } 61 | .ie7 input[type="checkbox"] { vertical-align: baseline; } 62 | .ie6 input { vertical-align: text-bottom; } 63 | label, input[type="button"], input[type="submit"], input[type="image"], button { cursor: pointer; } 64 | button, input, select, textarea { margin: 0; } 65 | input:valid, textarea:valid { } 66 | input:invalid, textarea:invalid { border-radius: 1px; -moz-box-shadow: 0px 0px 5px red; -webkit-box-shadow: 0px 0px 5px red; box-shadow: 0px 0px 5px red; } 67 | .no-boxshadow input:invalid, .no-boxshadow textarea:invalid { background-color: #f0dddd; } 68 | 69 | ::-moz-selection{ background: #FF5E99; color:#fff; text-shadow: none; } 70 | ::selection { background:#FF5E99; color:#fff; text-shadow: none; } 71 | a:link { -webkit-tap-highlight-color: #FF5E99; } 72 | 73 | button { width: auto; overflow: visible; } 74 | .ie7 img { -ms-interpolation-mode: bicubic; } 75 | 76 | body, select, input, textarea { color: #444; } 77 | h1, h2, h3, h4, h5, h6 { font-weight: bold; } 78 | a, a:active, a:visited { color: #607890; } 79 | a:hover { color: #036; } 80 | 81 | 82 | /* Bootstrap icons */ 83 | [class^="icon-"], [class*=" icon-"] { 84 | display: inline-block; 85 | width: 14px; 86 | height: 14px; 87 | line-height: 14px; 88 | vertical-align: text-top; 89 | background-image: url("../../img/glyphicons-halflings.png"); 90 | background-position: 14px 14px; 91 | background-repeat: no-repeat; 92 | *margin-right: .3em; 93 | } 94 | [class^="icon-"]:last-child, [class*=" icon-"]:last-child { 95 | *margin-left: 0; 96 | } 97 | .icon-white { 98 | background-image: url("../img/glyphicons-halflings-white.png"); 99 | } 100 | .icon-glass { 101 | background-position: 0 0; 102 | } 103 | .icon-music { 104 | background-position: -24px 0; 105 | } 106 | .icon-search { 107 | background-position: -48px 0; 108 | } 109 | .icon-envelope { 110 | background-position: -72px 0; 111 | } 112 | .icon-heart { 113 | background-position: -96px 0; 114 | } 115 | .icon-star { 116 | background-position: -120px 0; 117 | } 118 | .icon-star-empty { 119 | background-position: -144px 0; 120 | } 121 | .icon-user { 122 | background-position: -168px 0; 123 | } 124 | .icon-film { 125 | background-position: -192px 0; 126 | } 127 | .icon-th-large { 128 | background-position: -216px 0; 129 | } 130 | .icon-th { 131 | background-position: -240px 0; 132 | } 133 | .icon-th-list { 134 | background-position: -264px 0; 135 | } 136 | .icon-ok { 137 | background-position: -288px 0; 138 | } 139 | .icon-remove { 140 | background-position: -312px 0; 141 | } 142 | .icon-zoom-in { 143 | background-position: -336px 0; 144 | } 145 | .icon-zoom-out { 146 | background-position: -360px 0; 147 | } 148 | .icon-off { 149 | background-position: -384px 0; 150 | } 151 | .icon-signal { 152 | background-position: -408px 0; 153 | } 154 | .icon-cog { 155 | background-position: -432px 0; 156 | } 157 | .icon-trash { 158 | background-position: -456px 0; 159 | } 160 | .icon-home { 161 | background-position: 0 -24px; 162 | } 163 | .icon-file { 164 | background-position: -24px -24px; 165 | } 166 | .icon-time { 167 | background-position: -48px -24px; 168 | } 169 | .icon-road { 170 | background-position: -72px -24px; 171 | } 172 | .icon-download-alt { 173 | background-position: -96px -24px; 174 | } 175 | .icon-download { 176 | background-position: -120px -24px; 177 | } 178 | .icon-upload { 179 | background-position: -144px -24px; 180 | } 181 | .icon-inbox { 182 | background-position: -168px -24px; 183 | } 184 | .icon-play-circle { 185 | background-position: -192px -24px; 186 | } 187 | .icon-repeat { 188 | background-position: -216px -24px; 189 | } 190 | .icon-refresh { 191 | background-position: -240px -24px; 192 | } 193 | .icon-list-alt { 194 | background-position: -264px -24px; 195 | } 196 | .icon-lock { 197 | background-position: -287px -24px; 198 | } 199 | .icon-flag { 200 | background-position: -312px -24px; 201 | } 202 | .icon-headphones { 203 | background-position: -336px -24px; 204 | } 205 | .icon-volume-off { 206 | background-position: -360px -24px; 207 | } 208 | .icon-volume-down { 209 | background-position: -384px -24px; 210 | } 211 | .icon-volume-up { 212 | background-position: -408px -24px; 213 | } 214 | .icon-qrcode { 215 | background-position: -432px -24px; 216 | } 217 | .icon-barcode { 218 | background-position: -456px -24px; 219 | } 220 | .icon-tag { 221 | background-position: 0 -48px; 222 | } 223 | .icon-tags { 224 | background-position: -25px -48px; 225 | } 226 | .icon-book { 227 | background-position: -48px -48px; 228 | } 229 | .icon-bookmark { 230 | background-position: -72px -48px; 231 | } 232 | .icon-print { 233 | background-position: -96px -48px; 234 | } 235 | .icon-camera { 236 | background-position: -120px -48px; 237 | } 238 | .icon-font { 239 | background-position: -144px -48px; 240 | } 241 | .icon-bold { 242 | background-position: -167px -48px; 243 | } 244 | .icon-italic { 245 | background-position: -192px -48px; 246 | } 247 | .icon-text-height { 248 | background-position: -216px -48px; 249 | } 250 | .icon-text-width { 251 | background-position: -240px -48px; 252 | } 253 | .icon-align-left { 254 | background-position: -264px -48px; 255 | } 256 | .icon-align-center { 257 | background-position: -288px -48px; 258 | } 259 | .icon-align-right { 260 | background-position: -312px -48px; 261 | } 262 | .icon-align-justify { 263 | background-position: -336px -48px; 264 | } 265 | .icon-list { 266 | background-position: -360px -48px; 267 | } 268 | .icon-indent-left { 269 | background-position: -384px -48px; 270 | } 271 | .icon-indent-right { 272 | background-position: -408px -48px; 273 | } 274 | .icon-facetime-video { 275 | background-position: -432px -48px; 276 | } 277 | .icon-picture { 278 | background-position: -456px -48px; 279 | } 280 | .icon-pencil { 281 | background-position: 0 -72px; 282 | } 283 | .icon-map-marker { 284 | background-position: -24px -72px; 285 | } 286 | .icon-adjust { 287 | background-position: -48px -72px; 288 | } 289 | .icon-tint { 290 | background-position: -72px -72px; 291 | } 292 | .icon-edit { 293 | background-position: -96px -72px; 294 | } 295 | .icon-share { 296 | background-position: -120px -72px; 297 | } 298 | .icon-check { 299 | background-position: -144px -72px; 300 | } 301 | .icon-move { 302 | background-position: -168px -72px; 303 | } 304 | .icon-step-backward { 305 | background-position: -192px -72px; 306 | } 307 | .icon-fast-backward { 308 | background-position: -216px -72px; 309 | } 310 | .icon-backward { 311 | background-position: -240px -72px; 312 | } 313 | .icon-play { 314 | background-position: -264px -72px; 315 | } 316 | .icon-pause { 317 | background-position: -288px -72px; 318 | } 319 | .icon-stop { 320 | background-position: -312px -72px; 321 | } 322 | .icon-forward { 323 | background-position: -336px -72px; 324 | } 325 | .icon-fast-forward { 326 | background-position: -360px -72px; 327 | } 328 | .icon-step-forward { 329 | background-position: -384px -72px; 330 | } 331 | .icon-eject { 332 | background-position: -408px -72px; 333 | } 334 | .icon-chevron-left { 335 | background-position: -432px -72px; 336 | } 337 | .icon-chevron-right { 338 | background-position: -456px -72px; 339 | } 340 | .icon-plus-sign { 341 | background-position: 0 -96px; 342 | } 343 | .icon-minus-sign { 344 | background-position: -24px -96px; 345 | } 346 | .icon-remove-sign { 347 | background-position: -48px -96px; 348 | } 349 | .icon-ok-sign { 350 | background-position: -72px -96px; 351 | } 352 | .icon-question-sign { 353 | background-position: -96px -96px; 354 | } 355 | .icon-info-sign { 356 | background-position: -120px -96px; 357 | } 358 | .icon-screenshot { 359 | background-position: -144px -96px; 360 | } 361 | .icon-remove-circle { 362 | background-position: -168px -96px; 363 | } 364 | .icon-ok-circle { 365 | background-position: -192px -96px; 366 | } 367 | .icon-ban-circle { 368 | background-position: -216px -96px; 369 | } 370 | .icon-arrow-left { 371 | background-position: -240px -96px; 372 | } 373 | .icon-arrow-right { 374 | background-position: -264px -96px; 375 | } 376 | .icon-arrow-up { 377 | background-position: -289px -96px; 378 | } 379 | .icon-arrow-down { 380 | background-position: -312px -96px; 381 | } 382 | .icon-share-alt { 383 | background-position: -336px -96px; 384 | } 385 | .icon-resize-full { 386 | background-position: -360px -96px; 387 | } 388 | .icon-resize-small { 389 | background-position: -384px -96px; 390 | } 391 | .icon-plus { 392 | background-position: -408px -96px; 393 | } 394 | .icon-minus { 395 | background-position: -433px -96px; 396 | } 397 | .icon-asterisk { 398 | background-position: -456px -96px; 399 | } 400 | .icon-exclamation-sign { 401 | background-position: 0 -120px; 402 | } 403 | .icon-gift { 404 | background-position: -24px -120px; 405 | } 406 | .icon-leaf { 407 | background-position: -48px -120px; 408 | } 409 | .icon-fire { 410 | background-position: -72px -120px; 411 | } 412 | .icon-eye-open { 413 | background-position: -96px -120px; 414 | } 415 | .icon-eye-close { 416 | background-position: -120px -120px; 417 | } 418 | .icon-warning-sign { 419 | background-position: -144px -120px; 420 | } 421 | .icon-plane { 422 | background-position: -168px -120px; 423 | } 424 | .icon-calendar { 425 | background-position: -192px -120px; 426 | } 427 | .icon-random { 428 | background-position: -216px -120px; 429 | } 430 | .icon-comment { 431 | background-position: -240px -120px; 432 | } 433 | .icon-magnet { 434 | background-position: -264px -120px; 435 | } 436 | .icon-chevron-up { 437 | background-position: -288px -120px; 438 | } 439 | .icon-chevron-down { 440 | background-position: -313px -119px; 441 | } 442 | .icon-retweet { 443 | background-position: -336px -120px; 444 | } 445 | .icon-shopping-cart { 446 | background-position: -360px -120px; 447 | } 448 | .icon-folder-close { 449 | background-position: -384px -120px; 450 | } 451 | .icon-folder-open { 452 | background-position: -408px -120px; 453 | } 454 | .icon-resize-vertical { 455 | background-position: -432px -119px; 456 | } 457 | .icon-resize-horizontal { 458 | background-position: -456px -118px; 459 | } 460 | 461 | .ir { display: block; text-indent: -999em; overflow: hidden; background-repeat: no-repeat; text-align: left; direction: ltr; } 462 | .hidden { display: none; visibility: hidden; } 463 | .visuallyhidden { border: 0; clip: rect(0 0 0 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px; } 464 | .visuallyhidden.focusable:active, 465 | .visuallyhidden.focusable:focus { clip: auto; height: auto; margin: 0; overflow: visible; position: static; width: auto; } 466 | .invisible { visibility: hidden; } 467 | .clearfix:before, .clearfix:after { content: "\0020"; display: block; height: 0; overflow: hidden; } 468 | .clearfix:after { clear: both; } 469 | .clearfix { zoom: 1; } 470 | 471 | 472 | @media all and (orientation:portrait) { 473 | 474 | } 475 | 476 | @media all and (orientation:landscape) { 477 | 478 | } 479 | 480 | @media screen and (max-device-width: 480px) { 481 | 482 | /* html { -webkit-text-size-adjust:none; -ms-text-size-adjust:none; } */ 483 | } 484 | 485 | 486 | @media print { 487 | * { background: transparent !important; color: black !important; text-shadow: none !important; filter:none !important; 488 | -ms-filter: none !important; } 489 | a, a:visited { color: #444 !important; text-decoration: underline; } 490 | a[href]:after { content: " (" attr(href) ")"; } 491 | abbr[title]:after { content: " (" attr(title) ")"; } 492 | .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; } 493 | pre, blockquote { border: 1px solid #999; page-break-inside: avoid; } 494 | thead { display: table-header-group; } 495 | tr, img { page-break-inside: avoid; } 496 | @page { margin: 0.5cm; } 497 | p, h2, h3 { orphans: 3; widows: 3; } 498 | h2, h3{ page-break-after: avoid; } 499 | } -------------------------------------------------------------------------------- /_site/img/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karmi/elasticsearch-paramedic/0c8df1654d6574a6509c9599a63413dab046305b/_site/img/apple-touch-icon.png -------------------------------------------------------------------------------- /_site/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karmi/elasticsearch-paramedic/0c8df1654d6574a6509c9599a63413dab046305b/_site/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /_site/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karmi/elasticsearch-paramedic/0c8df1654d6574a6509c9599a63413dab046305b/_site/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /_site/img/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karmi/elasticsearch-paramedic/0c8df1654d6574a6509c9599a63413dab046305b/_site/img/spinner.gif -------------------------------------------------------------------------------- /_site/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Paramedic 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 77 |
78 | 79 |
80 | 86 |
87 |
88 | 89 |
90 | 96 | 117 |
118 |
119 | 120 |
121 | 127 | 198 |
199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | -------------------------------------------------------------------------------- /_site/js/app.js: -------------------------------------------------------------------------------- 1 | function l(m) { Ember.Logger.log(m); } 2 | 3 | var App = Em.Application.create({ 4 | name: "Paramedic", 5 | 6 | ready: function() { 7 | l(App.name + ' (re)loaded.') 8 | App.__initialize_page() 9 | App.__perform_refresh() 10 | App.__initialize_cubism() 11 | return this._super() 12 | }, 13 | 14 | elasticsearch_url: function() { 15 | var href = window.location.href.toString() 16 | 17 | return /_plugin/.test(href) ? href.substring(0, href.indexOf('/_plugin/')) : "http://localhost:9200" 18 | }(), 19 | 20 | refresh_intervals : Ember.ArrayController.create({ 21 | content: [ 22 | {label: '1 sec', value: 1000}, 23 | {label: '5 sec', value: 5000}, 24 | {label: '15 sec', value: 15*1000}, 25 | {label: '1 min', value: 60*1000}, 26 | {label: '5 min', value: 5*60*1000}, 27 | {label: '15 min', value: 15*60*1000} 28 | ] 29 | }), 30 | 31 | refresh_allowed: true, 32 | sounds_enabled: false, 33 | 34 | __perform_refresh: function() { 35 | App.cluster.__perform_refresh() 36 | App.nodes.__perform_refresh() 37 | App.indices.__perform_refresh() 38 | }, 39 | 40 | __initialize_cubism: function() { 41 | App.Cubism.setup() 42 | }, 43 | 44 | __initialize_page: function() { 45 | $("link[rel=apple-touch-icon]").attr("href", App.apple_touch_icon_b64) 46 | } 47 | }); 48 | 49 | App.refresh_interval = App.refresh_intervals.toArray()[1] 50 | 51 | // ===== Models =================================================================================== 52 | 53 | App.Cluster = Ember.Object.extend({ 54 | }); 55 | 56 | App.Node = Ember.Object.extend({ 57 | }); 58 | 59 | App.Index = Ember.Object.extend({ 60 | url: function() { 61 | return App.elasticsearch_url + '/' + this.name + '/_search?pretty' 62 | }.property("name").cacheable(), 63 | 64 | closed: function() { 65 | return (this.state && this.state == 'close') 66 | }.property("state").cacheable() 67 | }); 68 | 69 | App.Index.Shard = Ember.Object.extend({ 70 | }); 71 | 72 | // ===== Controllers ============================================================================== 73 | 74 | App.cluster = Ember.Object.create({ 75 | content: App.Cluster.create({}), 76 | 77 | refresh: function() { 78 | clearTimeout(App.cluster.poller) 79 | setTimeout(function() { App.set("refreshing", false) }, 1000) 80 | App.cluster.poller = setTimeout( function() { App.cluster.__perform_refresh() }, App.refresh_interval.value ) 81 | }, 82 | 83 | __perform_refresh: function() { 84 | if (!App.refresh_allowed) { return } 85 | var self = this; 86 | 87 | var __load_cluster_info = function(data) { 88 | App.cluster.setProperties(data) 89 | App.cluster.refresh(); 90 | } 91 | 92 | App.set("refreshing", true) 93 | $.getJSON(App.elasticsearch_url+"/_cluster/health", __load_cluster_info); 94 | } 95 | }); 96 | 97 | App.nodes = Ember.ArrayController.create({ 98 | hidden: false, 99 | content: [], 100 | 101 | contains: function(item) { 102 | return (Ember.typeOf(item) == 'string') ? this.mapProperty('id').contains(item) : this._super(); 103 | }, 104 | 105 | refresh: function() { 106 | if (App.nodes.hidden) { return } 107 | 108 | clearTimeout(App.nodes.poller) 109 | setTimeout(function() { App.set("refreshing", false) }, 1000) 110 | App.nodes.poller = setTimeout( function() { App.nodes.__perform_refresh() }, App.refresh_interval.value ) 111 | }, 112 | 113 | __perform_refresh: function() { 114 | if (!App.refresh_allowed) { return } 115 | var self = this; 116 | 117 | var __load_nodes_info = function(data) { 118 | for (var node_id in data.nodes) { 119 | if ( !self.contains(node_id) ) self.addObject(App.Node.create({ id: node_id })) 120 | var node = self.findProperty("id", node_id) 121 | .set("name", data.nodes[node_id]['name']) 122 | .set("hostname", data.nodes[node_id]['host']) 123 | .set("http_address", data.nodes[node_id]['http_address']) 124 | .set("jvm_heap_max", (data.nodes[node_id]['jvm']['mem']['heap_max_in_bytes']/1000000).toFixed(2) + " MB") 125 | .set("start_time", data.nodes[node_id]['jvm']['start_time']) 126 | } 127 | 128 | // Remove missing nodes from the collection 129 | // TODO: Use model instance identity, contains(), etc 130 | // 131 | self.forEach(function(item) { 132 | var loc = self.content.length || 0 133 | while(--loc >= 0) { 134 | var curObject = self.content.objectAt(loc) 135 | if ( item && !Ember.keys(data.nodes).contains(item.id) && curObject.id === item.id) { 136 | self.content.removeAt(loc) 137 | } 138 | } 139 | }) 140 | 141 | App.nodes.refresh(); 142 | }; 143 | 144 | var __load_nodes_stats = function(data) { 145 | for (var node_id in data.nodes) { 146 | var node = self.findProperty("id", node_id) 147 | if (node) { 148 | node 149 | .set("disk", (data.nodes[node_id]['indices']['store']['size_in_bytes']/1000000).toFixed(2) + " MB") 150 | .set("docs", data.nodes[node_id]['indices']['docs']['count']) 151 | .set("load", data.nodes[node_id]['os']['load_average'].toFixed(3)) 152 | .set("cpu", data.nodes[node_id]['process']['cpu']['percent']) 153 | .set("jvm_heap_used", (data.nodes[node_id]['jvm']['mem']['heap_used_in_bytes']/1000000).toFixed(2)) 154 | } 155 | } 156 | }; 157 | 158 | App.set("refreshing", true) 159 | $.getJSON(App.elasticsearch_url+"/_nodes?jvm", __load_nodes_info); 160 | $.getJSON(App.elasticsearch_url+"/_nodes/stats?indices&os&process&jvm", __load_nodes_stats); 161 | } 162 | }); 163 | 164 | App.indices = Ember.ArrayController.create({ 165 | hidden: false, 166 | content: [], 167 | 168 | contains: function(item) { 169 | return (Ember.typeOf(item) == 'string') ? this.mapProperty('name').contains(item) : this._super(); 170 | }, 171 | 172 | refresh: function() { 173 | if (App.indices.hidden) { return } 174 | 175 | clearTimeout(App.indices.poller) 176 | setTimeout(function() { App.set("refreshing", false) }, 1000) 177 | App.indices.poller = setTimeout( function() { App.indices.__perform_refresh() }, App.refresh_interval.value ) 178 | }, 179 | 180 | sorted: function() { 181 | return this.get("content") 182 | .toArray() 183 | .sort(function(a,b) { if (a.name < b.name) return -1; if (a.name > b.name) return 1; return 0; }) 184 | }.property("content.@each").cacheable(), 185 | 186 | showDetail: function(event) { 187 | // l(event.context.name) 188 | // l(this) 189 | event.context.toggleProperty("show_detail") 190 | }, 191 | 192 | __perform_refresh: function() { 193 | if (!App.refresh_allowed) { return } 194 | var self = this; 195 | 196 | var __load_cluster_state = function(data) { 197 | for (var index_name in data.metadata.indices) { 198 | // Mark master node 199 | // 200 | var master_node = App.nodes.content.findProperty("id", data.master_node) 201 | if (master_node) master_node.set("master", true) 202 | 203 | // Create or find an index 204 | // 205 | if ( !self.contains(index_name) ) self.addObject(App.Index.create({ name: index_name })) 206 | var index = self.findProperty("name", index_name) 207 | 208 | // Update index properties 209 | // 210 | index 211 | .set("state", data.metadata.indices[index_name]['state']) 212 | 213 | .set("settings", Ember.Object.create({ 214 | number_of_replicas: data.metadata.indices[index_name]['settings']['index.number_of_replicas'], 215 | number_of_shards: data.metadata.indices[index_name]['settings']['index.number_of_shards'] 216 | })) 217 | 218 | .set("aliases", data.metadata.indices[index_name]['aliases']) 219 | 220 | // Shards 221 | // 222 | var shards = [], 223 | primaries = [], 224 | replicas = [], 225 | unassigned = [] 226 | 227 | index 228 | .set("shards", function() { 229 | if (data.routing_table.indices[index_name]) { 230 | 231 | for (var shard_name in data.routing_table.indices[index_name]['shards']) { 232 | 233 | data.routing_table.indices[index_name]['shards'][shard_name].forEach(function(s) { 234 | 235 | var shard = App.Index.Shard.create({name: shard_name}) 236 | shard.set("state", s.state) 237 | .set("primary", s.primary) 238 | .set("index", s.index) 239 | .set("node_id", s.node) 240 | .set("relocating_node_id", s.relocating_node) 241 | 242 | if (s.primary) primaries .addObject(shard) 243 | if (!s.primary && s.node) replicas .addObject(shard) 244 | if (!s.primary && !s.node) unassigned.addObject(shard) 245 | }); 246 | 247 | } 248 | } 249 | 250 | // Sort unassingned shards to series [0 .. n, 0 .. n] 251 | // [0, 0, 1, 1, 2, 2] becomes: [0, 1, 2, 0, 1, 2] 252 | // 253 | var unassigned_sorted = [] 254 | unassigned_sorted.length = unassigned.length 255 | 256 | var num_shards = primaries.length, 257 | num_replicas = unassigned.length/num_shards; 258 | 259 | for (var i = 0; i < num_shards; i++) { 260 | // Create slices: [0, 0]; [1, 1]; [2, 2] 261 | unassigned.slice(i*num_replicas, i*num_replicas+num_replicas).forEach(function(item,index) { 262 | // Position for first slices: 0, 3 263 | // Position for second slices: 1, 4 264 | // Position for third slices: 2, 5 265 | var position = i + num_shards * index 266 | unassigned_sorted[position] = item 267 | }) 268 | }; 269 | 270 | unassigned_sorted = unassigned_sorted.filter(function(i){return i != null}) 271 | return shards.concat(primaries, replicas, unassigned_sorted) 272 | }()) 273 | 274 | if (index.show_detail) { 275 | index.set("nodes", function() { 276 | var nodes = [] 277 | if (data.routing_table.indices[index_name]) { 278 | for (var shard_name in data.routing_table.indices[index_name]['shards']) { 279 | 280 | data.routing_table.indices[index_name]['shards'][shard_name].forEach(function(shard_data) { 281 | if (shard_data.node) { 282 | 283 | // Find the node 284 | // var node = App.nodes.content.findProperty("id", shard_data.node) 285 | var node = nodes.findProperty("id", shard_data.node) 286 | if (!node) { 287 | var node = App.Node.create( App.nodes.content.findProperty("id", shard_data.node) ) 288 | nodes.addObject(node) 289 | } 290 | 291 | // Initialize node.shards 292 | if (node && !node.shards) node.set("shards", []) 293 | 294 | // Find shard in index.shards 295 | var shard = index.shards.find(function(item) { 296 | return item.name == shard_data.shard && item.node_id == shard_data.node && item.index == shard_data.index 297 | }) 298 | 299 | // Remove shard from node.shards 300 | node.shards.forEach(function(item, index) { 301 | if (item.name == shard_data.shard && item.node_id == shard_data.node && item.index == shard_data.index) { 302 | node.shards.removeAt(index) 303 | } 304 | }) 305 | 306 | // Add (possibly updated) shard back into collection 307 | if (shard) { node.shards.addObject(shard) } 308 | 309 | node.set("shards", node.shards.sort(function(a,b) { return a.name > b.name; })) 310 | } 311 | }); 312 | }; 313 | } 314 | index.set("show_detail_loaded", true) 315 | return nodes 316 | }()) 317 | } 318 | 319 | // Remove deleted indices from the collection 320 | // TODO: Use model instance identity for this 321 | // 322 | self.forEach(function(item) { 323 | // console.log(item.name) 324 | var loc = self.content.length || 0 325 | while(--loc >= 0) { 326 | var curObject = self.content.objectAt(loc) 327 | if ( item && !Ember.keys(data.metadata.indices).contains(item.name) && curObject.name === item.name) { 328 | self.content.removeAt(loc) 329 | } 330 | } 331 | }) 332 | 333 | } 334 | }; 335 | 336 | var __load_indices_stats = function(data) { 337 | App.cluster.set("docs_count", 338 | data._all.primaries.docs ? data._all.primaries.docs.count : 0) 339 | 340 | var indices = data._all.indices || data.indices 341 | 342 | for (var index_name in indices) { 343 | var index = self.findProperty("name", index_name) 344 | if (!index) continue 345 | 346 | index 347 | .set("size", indices[index_name]['primaries']['store']['size']) 348 | .set("size_in_bytes", indices[index_name]['primaries']['store']['size_in_bytes']) 349 | .set("docs", indices[index_name]['primaries']['docs']['count']) 350 | .set("indexing", indices[index_name]['primaries']['indexing']) 351 | .set("search", indices[index_name]['primaries']['search']) 352 | .set("get", indices[index_name]['primaries']['get']) 353 | } 354 | }; 355 | 356 | var __load_indices_recovery = function(data) { 357 | for (var index_name in data.indices) { 358 | var index = self.findProperty("name", index_name) 359 | if (!index) continue 360 | if (!index.show_detail) continue 361 | 362 | for (var shard_name in data.indices[index_name]['shards']) { 363 | // var shard = index.shards.findProperty("name", shard_name) 364 | 365 | data.indices[index_name]['shards'][shard_name].forEach(function(shard_data) { 366 | var shard = index.shards.find(function(item) { 367 | return item.name == shard_name && item.node_id == shard_data['routing']['node'] 368 | }) 369 | // if (!shard) continue 370 | if (shard) { 371 | 372 | // l(shard_data) 373 | shard 374 | .set("size", shard_data.index.size) 375 | // .set("docs", shard_data.docs.num_docs) 376 | shard 377 | .set("recovery", function() { 378 | var recovery_type = shard_data['peer_recovery'] ? 'peer_recovery' : 'gateway_recovery' 379 | 380 | return { 381 | stage: shard_data[recovery_type].stage, 382 | time: shard_data[recovery_type].time, 383 | progress: shard_data[recovery_type].index.progress, 384 | size: shard_data[recovery_type].index.size, 385 | reused_size: shard_data[recovery_type].index.reused_size 386 | } 387 | }()) 388 | } 389 | }); 390 | } 391 | } 392 | }; 393 | 394 | App.set("refreshing", true) 395 | $.getJSON(App.elasticsearch_url+"/_cluster/state", __load_cluster_state); 396 | $.getJSON(App.elasticsearch_url+"/_stats", __load_indices_stats); 397 | $.getJSON(App.elasticsearch_url+"/_recovery", __load_indices_recovery); 398 | 399 | // Schedule next run 400 | // 401 | App.indices.refresh(); 402 | } 403 | }); 404 | 405 | // ===== Views ================================================================================== 406 | 407 | App.toggleRefreshAllowedButton = Ember.View.create({ 408 | text: 'Stop', 409 | 410 | toggle: function(event) { 411 | this.set("text", ( App.refresh_allowed == true ) ? 'Start' : 'Stop') 412 | App.toggleProperty("refresh_allowed") 413 | } 414 | }); 415 | 416 | App.toggleChart = Ember.View.create({ 417 | text: 'Hide', 418 | 419 | toggle: function(event) { 420 | var chart = $("#chart"), 421 | visible = chart.is(":visible") 422 | 423 | this.set("text", visible ? 'Show' : 'Hide') 424 | visible ? chart.hide('fast') : chart.show('fast') 425 | App.nodes.refresh(); 426 | } 427 | }); 428 | 429 | App.toggleIndices = Ember.View.create({ 430 | hidden: false, 431 | text: 'Hide', 432 | 433 | toggle: function(event) { 434 | this.set("text", this.get('hidden') ? 'Hide' : 'Show') 435 | this.toggleProperty('hidden') 436 | App.indices.toggleProperty('hidden') 437 | App.indices.refresh(); 438 | } 439 | }); 440 | 441 | App.toggleNodes = Ember.View.create({ 442 | hidden: false, 443 | text: 'Hide', 444 | 445 | toggle: function(event) { 446 | this.set("text", this.get('hidden') ? 'Hide' : 'Show') 447 | this.toggleProperty('hidden') 448 | App.nodes.toggleProperty('hidden') 449 | } 450 | }); 451 | 452 | // ===== Observers ============================================================================== 453 | 454 | App.addObserver('elasticsearch_url', function(event) { 455 | // TODO: Use the `blur` event, so we're not trying to load partial URLs 456 | Ember.Logger.log("ElasticSearch URL changed to " + this.get("elasticsearch_url")) 457 | App.cluster.set("content", App.Cluster.create({})) 458 | App.nodes.set("content", []) 459 | App.indices.set("content", []) 460 | App.ready() 461 | App.Cubism.reset() 462 | }); 463 | 464 | App.addObserver('refresh_interval', function() { 465 | Ember.Logger.log("Refresh interval changed to " + App.refresh_interval.label) 466 | App.ready() 467 | }); 468 | 469 | App.addObserver('refresh_allowed', function() { 470 | App.refresh_allowed ? App.Cubism.start() : App.Cubism.stop() 471 | App.__perform_refresh() 472 | }); 473 | 474 | App.nodes.addObserver('@each.name', function() { 475 | // Wait until we have node names... 476 | if ( !App.nodes.everyProperty("name") ) return; 477 | 478 | Ember.Logger.log("Nodes changed to: " + App.nodes.mapProperty("name").join("; ")) 479 | App.Cubism.reset() 480 | }); 481 | 482 | App.cluster.addObserver('cluster_name', function() { 483 | $('title').text('Paramedic | ' + this.get('cluster_name')) 484 | }); 485 | 486 | App.cluster.addObserver('status', function() { 487 | if (App.get("sounds_enabled")) { 488 | // FIXME: When running as a plugin, audio won't play again when `var a = $('#alert-'+this.get("status"))[0]` 489 | var a = new Audio('audio/alert-'+this.get("status")+'.mp3') 490 | a.volume=0.7 491 | a.play() 492 | } 493 | }); 494 | 495 | // ===== Helpers ================================================================================ 496 | 497 | Handlebars.registerHelper('number_with_delimiter', function(property) { 498 | var delimiter = ' ' 499 | , value = (isNaN(this)) ? Ember.getPath(this, property) : this.toString(); 500 | // console.log(this, property, value) 501 | // Credit: http://stackoverflow.com/a/2254896/95696 502 | return value ? value.toString().replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1"+delimiter) : value 503 | }); 504 | 505 | // ===== Varia ================================================================================== 506 | 507 | App.apple_touch_icon_b64 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJAAAACQCAIAAABoJHXvAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAACB9JREFUeNrsXb9TKjEQ5jE30mgljVZUvEYrKmm0osLKv9NKKypsoLKCBioqrsFKG21831zm3TD+IHvJJpdc9ive4LyDu82X3exuNnt/Pj8/W4J40JYhEMIEQphACBPCBEKYQAgTwgRCmEAIE8IEQphACGseshAeYrvdlp87nc7p6Wk4A/Ty8vL+/l7+eX5+nhxhHx8fYCjPc4zFbrfDn/v/ixEZj8fhEDafz/fnU/mQpwV6vd7R0VEzCQMxm81msViAp9jt0raA+gzaLi8vvTGX+aFqUeCLMjUDmH/T6RRsXRZwTZtzwtbr9Ww2ayRVXybl8/MzJuVgMABtURIGGTD1YAbTceEgMtY8LM83NzeOVK3t7tEfHh6SYqsEpIbsjoxK2x1bDXAubBY2R5w5ISxxtpxyxk8YjLiwVXIGhytowhCdwFMSqvad5O9xd0CEPT09CUlOx+QPY+UvZhP8eOLF8Hp7vZ5K8ISWPzxg4t7f318KwBWkr0/w8vv9fnBxGCJHIlWuo0tHULOqzP/C+ENkCm24jIuwNuPse319pch8d3cXI1vfASkgC8U2YGS4VjI2wmAPKWzd3t6enJw0Zn2CLJCIwhllfLwSpk1qwBKORiPPmxEeAInAmVYurqQPD2EUewgD0iTd+sKZ1shjqWMJT3kIoxhorlU3TFCkY1EyNg3Trl5NVa9yMdOuZAFpmPZRmq1eRBlZHEVPhNVeu+IBvV5Pu4zZ54LbHtiimItmWEWt2d/tdvUTtl8F9pskrTSgnZdvb28RaNjZ2ZkQVqY86ies8QU2lQIy17fwQZhoWIk8zy1vwZCtr3d/GdNlNptVzdTBo3NX2BS6htWL6XRqkFfdbDaTySRGeX0Q1ul03KmXcb4HYSy7beh2u00gzF0QZhnWaAOSZjodAlnDBEJYGAgiNWUfvaeDIJK/QpiYxCbDcn63Xd8+hY2VfWh3/kInzF3UHCkso3VbwrTZzOPj46T40MpbM2GU7eakCNPKa5mwtyVMW1iSzt4KUd7XAvUQBra0gYWHfGhQoMhrU7ZtRZj2xnARm1ebfRiQV+sY10MY9Fp74xSq28w8e2POzAmjnAZLoX7UTGriwTI2wjabDcUephY10wWHkpmdVzchTHVX0l7WjFN7ZqDIvi5Q9ZczA7Yo7ScQjtDt4ZeehN6yBjZfP7xQQXYYPa37jnmPawaDAf2+FQ6lq6ZsxIPMo9FIW2uufnMymfA2RvBm9w6f44NQj4+PRO6vr6+JGQYSYcqrAYgRH6gCYUTPhTgDwnQubm5uDisQ3ejh12BItYtf9lso/vb2pj6oLgeVpt5hMRjzNPVC+/DD4ZA+ekoloLKq3amK574fsMh+/KbxrMdtIi3QNItEtaMBS3N/f0/34FXhXlm7NyjAlun47mgQj9SnAzUmjBlwNsKgyMSmFQmGZRgZrqQPQ219vJ1tvAFDNB6P6Z1zXBHmrTNxY6Lpv3//WvarNiSs9x9ClZk12vyHpzUMDhJ7YXpSwOiZbWMaahhii3kBhHuYMqnVAdhEAljGbPbDbJ0OFe59DxcE38GS1sm4HgXmGEGiqNpvijWZTALqhKOMJEJ6ac/semQ4Mx3Srv5Htnibof9gEuFHqFqtPM+rJn/VdgkCe3H3y9GoxBbWFAy++rf1U5Vj9uN31HVlNgW0LZfL1WpFuTcunk6nxO2VZkPtTxLjM8TUFxcXWicgI9J+dXUFP5D48hsVElI2MKPG4cQpPS6u1IOiwhqmNguIe10I0SiXYU7FS9jhGgjiCGA8K3XWbRs8JYUzYumdmlzRBQPafDdxd96gn31mNrPghmhf2UFs1t4vYDZw9LqJHzEejx2VulLeZwK+DQQ3dOuxpGnVQnmYabrylEM9Zrkh8zgMnGmv4WrWHhcoUoMts8jHnDAsP1olS/MNfdqqvUpFm5yZDq2PZ3kWKtJgWWsPbXxjK8IokVZqyxildYpNhGpFmLdm7RFBW6xo2cLfNvmrdYtTI8x1HwxbwrS3T60jsLZvds2EabscSOlHWIRpz2CnZhK1Pr3lKX1bwmTfy/OISXOwyMBAWJqtAuoySKJhXmHfZkYIS88kCppGWIxnzs3gIUsQt4aF1j3Tvml2wwmzbD4WY2E5A2H1xs7D4dDsi5G+VzpjmeaHd5bzPHcXq/X7/ePj4/V6TX9bIa7H87hoXObhZYVZK36cFxCno4KGHb4g6u4pvBpmb4R9rGEefKdYCLPvMe4jl0ipS2kGW9rt5lBSU1pNTyF2plS3hZL8ddqWOBZQWrSGEjhrvVWKuYgalLp0lhb+PIRRvOrlcpmyerWYNg7ZTKJ2GVutVk1VMsilPa5CqeH0R1iLUM1qcOA3ChDl4nqlCRthlEyPOlLfJBefLhFXtzs2wohd6lXTCvsudCEoFqQgssVlD1uVumpTFl5KP/t9K6poRnTS6XQCb45Z9mrP8xyfK52kMjga64MwwL7lC1yp8XgcDk+Pj4+WUb/qSMr1PMwbmJRjmamBd0yYCYN+SC/ZL74G79ZP28WEktLS0hiymxwnNR2j0Ujaa6t3fbD/rBPC4PUl3sBe+2aWsAgrOUv2hW+O2Go5relQr/VAsDWfzxPpJaC6qDntiua8CAdPDx9ksVjQX44UI7y18M/8CKO6OIMzs2btIUP17/dm/L2WuZV9wLbbbV4gxs4rqgHoWQH/AQxzasoMHx8fZWVVaEnF/dc9drvd2o8IB0GYoH63XiCECYQwIUwghAmEMCFMIIQJhDAhTCCECYQwIUxQI/4JMABsmpGrX6NFBgAAAABJRU5ErkJggg=="; 508 | -------------------------------------------------------------------------------- /_site/js/cubism.js: -------------------------------------------------------------------------------- 1 | var App = App || Em.Application.create({}); 2 | 3 | App.Cubism = Ember.Object.create({ 4 | 5 | // ElasticSearch extension instance 6 | elasticsearch: {}, 7 | 8 | // Cubism.js context 9 | context: {}, 10 | 11 | // Chart canvas 12 | chart: d3.select("#chart").append("div").attr("id", "chart-inner"), 13 | 14 | // Function to add new horizon chart 15 | add_chart: function(metrics, options) { 16 | var options = options || {colors: 'Greens'} 17 | 18 | this.chart.selectAll(".horizon") 19 | .data(metrics, function(d) { return d.toString(); }) 20 | .enter().append("div") 21 | .attr("class", "horizon") 22 | .call(this.context.horizon() 23 | .height(25) 24 | .colors(function() { return colorbrewer[options['colors']][8] }) 25 | ) 26 | return this.chart; 27 | }, 28 | 29 | // Setup the whole chart 30 | setup: function() { 31 | var self = this 32 | 33 | // Setup context 34 | self.__setup_context() 35 | 36 | // Top axis 37 | if ( d3.select("#chart .axis.top").empty() ) 38 | self.chart.append("div") 39 | .attr("class", "axis top") 40 | .call(self.context.axis().orient("top")); 41 | 42 | // Rule 43 | if ( d3.select("#chart .rule").empty() ) 44 | self.chart.append("div") 45 | .attr("class", "rule") 46 | .call(self.context.rule()); 47 | 48 | // Move the rule with mouse 49 | self.context.on("focus", function(i) { d3.selectAll(".value").style("right", i == null ? null : self.context.size() - i + "px"); }) 50 | 51 | // Draw the chart(s) 52 | self.__draw() 53 | 54 | return self 55 | }, 56 | 57 | // Remove the chart and re-draw it 58 | reset: function() { 59 | var self = this 60 | 61 | // Re-insert the chart element 62 | d3.select("#chart-inner").remove() 63 | self.chart = d3.select("#chart").append("div").attr("id", "chart-inner") 64 | 65 | // Re-initialize everything 66 | self.setup() 67 | return self 68 | }, 69 | 70 | // Start the polling/animation 71 | start: function() { 72 | if (this.context.start) this.context.start() 73 | }, 74 | 75 | // Stop the polling/animation 76 | stop: function() { 77 | if (this.context.stop) this.context.stop() 78 | }, 79 | 80 | // Draw the metrics 81 | __draw: function() { 82 | var self = this 83 | 84 | self.elasticsearch = cubism.elasticsearch(self.context, {host: App.elasticsearch_url}, function() { 85 | [ 86 | { metrics: this.metrics("os.cpu_percent"), colors: 'Greens' }, 87 | { metrics: this.metrics("process.cpu.percent"), colors: 'Greens' }, 88 | { metrics: this.metrics("jvm.mem.heap_used_in_bytes"), colors: 'Blues' }, 89 | { metrics: this.metrics("http.current_open"), colors: 'Oranges' }, 90 | { metrics: this.metrics("indices.indexing.index_current"), colors: 'Spectral' }, 91 | { metrics: this.metrics("indices.search.query_current"), colors: 'YlOrRd' } 92 | ].forEach( 93 | function(group) { self.add_chart(group.metrics, {colors: group.colors}) } 94 | ) 95 | 96 | return self 97 | }); 98 | }, 99 | 100 | __setup_context: function() { 101 | this.context = cubism.context() 102 | .serverDelay(0) 103 | .clientDelay(0) 104 | .step(1000) 105 | .size($("#chart").width()) 106 | } 107 | 108 | }); 109 | -------------------------------------------------------------------------------- /_site/js/libs/colorbrewer.min.js: -------------------------------------------------------------------------------- 1 | var colorbrewer={YlGn:{3:["rgb(247,252,185)","rgb(173,221,142)","rgb(49,163,84)"],4:["rgb(255,255,204)","rgb(194,230,153)","rgb(120,198,121)","rgb(35,132,67)"],5:["rgb(255,255,204)","rgb(194,230,153)","rgb(120,198,121)","rgb(49,163,84)","rgb(0,104,55)"],6:["rgb(255,255,204)","rgb(217,240,163)","rgb(173,221,142)","rgb(120,198,121)","rgb(49,163,84)","rgb(0,104,55)"],7:["rgb(255,255,204)","rgb(217,240,163)","rgb(173,221,142)","rgb(120,198,121)","rgb(65,171,93)","rgb(35,132,67)","rgb(0,90,50)"],8:["rgb(255,255,229)","rgb(247,252,185)","rgb(217,240,163)","rgb(173,221,142)","rgb(120,198,121)","rgb(65,171,93)","rgb(35,132,67)","rgb(0,90,50)"],9:["rgb(255,255,229)","rgb(247,252,185)","rgb(217,240,163)","rgb(173,221,142)","rgb(120,198,121)","rgb(65,171,93)","rgb(35,132,67)","rgb(0,104,55)","rgb(0,69,41)"]},YlGnBu:{3:["rgb(237,248,177)","rgb(127,205,187)","rgb(44,127,184)"],4:["rgb(255,255,204)","rgb(161,218,180)","rgb(65,182,196)","rgb(34,94,168)"],5:["rgb(255,255,204)","rgb(161,218,180)","rgb(65,182,196)","rgb(44,127,184)","rgb(37,52,148)"],6:["rgb(255,255,204)","rgb(199,233,180)","rgb(127,205,187)","rgb(65,182,196)","rgb(44,127,184)","rgb(37,52,148)"],7:["rgb(255,255,204)","rgb(199,233,180)","rgb(127,205,187)","rgb(65,182,196)","rgb(29,145,192)","rgb(34,94,168)","rgb(12,44,132)"],8:["rgb(255,255,217)","rgb(237,248,177)","rgb(199,233,180)","rgb(127,205,187)","rgb(65,182,196)","rgb(29,145,192)","rgb(34,94,168)","rgb(12,44,132)"],9:["rgb(255,255,217)","rgb(237,248,177)","rgb(199,233,180)","rgb(127,205,187)","rgb(65,182,196)","rgb(29,145,192)","rgb(34,94,168)","rgb(37,52,148)","rgb(8,29,88)"]},GnBu:{3:["rgb(224,243,219)","rgb(168,221,181)","rgb(67,162,202)"],4:["rgb(240,249,232)","rgb(186,228,188)","rgb(123,204,196)","rgb(43,140,190)"],5:["rgb(240,249,232)","rgb(186,228,188)","rgb(123,204,196)","rgb(67,162,202)","rgb(8,104,172)"],6:["rgb(240,249,232)","rgb(204,235,197)","rgb(168,221,181)","rgb(123,204,196)","rgb(67,162,202)","rgb(8,104,172)"],7:["rgb(240,249,232)","rgb(204,235,197)","rgb(168,221,181)","rgb(123,204,196)","rgb(78,179,211)","rgb(43,140,190)","rgb(8,88,158)"],8:["rgb(247,252,240)","rgb(224,243,219)","rgb(204,235,197)","rgb(168,221,181)","rgb(123,204,196)","rgb(78,179,211)","rgb(43,140,190)","rgb(8,88,158)"],9:["rgb(247,252,240)","rgb(224,243,219)","rgb(204,235,197)","rgb(168,221,181)","rgb(123,204,196)","rgb(78,179,211)","rgb(43,140,190)","rgb(8,104,172)","rgb(8,64,129)"]},BuGn:{3:["rgb(229,245,249)","rgb(153,216,201)","rgb(44,162,95)"],4:["rgb(237,248,251)","rgb(178,226,226)","rgb(102,194,164)","rgb(35,139,69)"],5:["rgb(237,248,251)","rgb(178,226,226)","rgb(102,194,164)","rgb(44,162,95)","rgb(0,109,44)"],6:["rgb(237,248,251)","rgb(204,236,230)","rgb(153,216,201)","rgb(102,194,164)","rgb(44,162,95)","rgb(0,109,44)"],7:["rgb(237,248,251)","rgb(204,236,230)","rgb(153,216,201)","rgb(102,194,164)","rgb(65,174,118)","rgb(35,139,69)","rgb(0,88,36)"],8:["rgb(247,252,253)","rgb(229,245,249)","rgb(204,236,230)","rgb(153,216,201)","rgb(102,194,164)","rgb(65,174,118)","rgb(35,139,69)","rgb(0,88,36)"],9:["rgb(247,252,253)","rgb(229,245,249)","rgb(204,236,230)","rgb(153,216,201)","rgb(102,194,164)","rgb(65,174,118)","rgb(35,139,69)","rgb(0,109,44)","rgb(0,68,27)"]},PuBuGn:{3:["rgb(236,226,240)","rgb(166,189,219)","rgb(28,144,153)"],4:["rgb(246,239,247)","rgb(189,201,225)","rgb(103,169,207)","rgb(2,129,138)"],5:["rgb(246,239,247)","rgb(189,201,225)","rgb(103,169,207)","rgb(28,144,153)","rgb(1,108,89)"],6:["rgb(246,239,247)","rgb(208,209,230)","rgb(166,189,219)","rgb(103,169,207)","rgb(28,144,153)","rgb(1,108,89)"],7:["rgb(246,239,247)","rgb(208,209,230)","rgb(166,189,219)","rgb(103,169,207)","rgb(54,144,192)","rgb(2,129,138)","rgb(1,100,80)"],8:["rgb(255,247,251)","rgb(236,226,240)","rgb(208,209,230)","rgb(166,189,219)","rgb(103,169,207)","rgb(54,144,192)","rgb(2,129,138)","rgb(1,100,80)"],9:["rgb(255,247,251)","rgb(236,226,240)","rgb(208,209,230)","rgb(166,189,219)","rgb(103,169,207)","rgb(54,144,192)","rgb(2,129,138)","rgb(1,108,89)","rgb(1,70,54)"]},PuBu:{3:["rgb(236,231,242)","rgb(166,189,219)","rgb(43,140,190)"],4:["rgb(241,238,246)","rgb(189,201,225)","rgb(116,169,207)","rgb(5,112,176)"],5:["rgb(241,238,246)","rgb(189,201,225)","rgb(116,169,207)","rgb(43,140,190)","rgb(4,90,141)"],6:["rgb(241,238,246)","rgb(208,209,230)","rgb(166,189,219)","rgb(116,169,207)","rgb(43,140,190)","rgb(4,90,141)"],7:["rgb(241,238,246)","rgb(208,209,230)","rgb(166,189,219)","rgb(116,169,207)","rgb(54,144,192)","rgb(5,112,176)","rgb(3,78,123)"],8:["rgb(255,247,251)","rgb(236,231,242)","rgb(208,209,230)","rgb(166,189,219)","rgb(116,169,207)","rgb(54,144,192)","rgb(5,112,176)","rgb(3,78,123)"],9:["rgb(255,247,251)","rgb(236,231,242)","rgb(208,209,230)","rgb(166,189,219)","rgb(116,169,207)","rgb(54,144,192)","rgb(5,112,176)","rgb(4,90,141)","rgb(2,56,88)"]},BuPu:{3:["rgb(224,236,244)","rgb(158,188,218)","rgb(136,86,167)"],4:["rgb(237,248,251)","rgb(179,205,227)","rgb(140,150,198)","rgb(136,65,157)"],5:["rgb(237,248,251)","rgb(179,205,227)","rgb(140,150,198)","rgb(136,86,167)","rgb(129,15,124)"],6:["rgb(237,248,251)","rgb(191,211,230)","rgb(158,188,218)","rgb(140,150,198)","rgb(136,86,167)","rgb(129,15,124)"],7:["rgb(237,248,251)","rgb(191,211,230)","rgb(158,188,218)","rgb(140,150,198)","rgb(140,107,177)","rgb(136,65,157)","rgb(110,1,107)"],8:["rgb(247,252,253)","rgb(224,236,244)","rgb(191,211,230)","rgb(158,188,218)","rgb(140,150,198)","rgb(140,107,177)","rgb(136,65,157)","rgb(110,1,107)"],9:["rgb(247,252,253)","rgb(224,236,244)","rgb(191,211,230)","rgb(158,188,218)","rgb(140,150,198)","rgb(140,107,177)","rgb(136,65,157)","rgb(129,15,124)","rgb(77,0,75)"]},RdPu:{3:["rgb(253,224,221)","rgb(250,159,181)","rgb(197,27,138)"],4:["rgb(254,235,226)","rgb(251,180,185)","rgb(247,104,161)","rgb(174,1,126)"],5:["rgb(254,235,226)","rgb(251,180,185)","rgb(247,104,161)","rgb(197,27,138)","rgb(122,1,119)"],6:["rgb(254,235,226)","rgb(252,197,192)","rgb(250,159,181)","rgb(247,104,161)","rgb(197,27,138)","rgb(122,1,119)"],7:["rgb(254,235,226)","rgb(252,197,192)","rgb(250,159,181)","rgb(247,104,161)","rgb(221,52,151)","rgb(174,1,126)","rgb(122,1,119)"],8:["rgb(255,247,243)","rgb(253,224,221)","rgb(252,197,192)","rgb(250,159,181)","rgb(247,104,161)","rgb(221,52,151)","rgb(174,1,126)","rgb(122,1,119)"],9:["rgb(255,247,243)","rgb(253,224,221)","rgb(252,197,192)","rgb(250,159,181)","rgb(247,104,161)","rgb(221,52,151)","rgb(174,1,126)","rgb(122,1,119)","rgb(73,0,106)"]},PuRd:{3:["rgb(231,225,239)","rgb(201,148,199)","rgb(221,28,119)"],4:["rgb(241,238,246)","rgb(215,181,216)","rgb(223,101,176)","rgb(206,18,86)"],5:["rgb(241,238,246)","rgb(215,181,216)","rgb(223,101,176)","rgb(221,28,119)","rgb(152,0,67)"],6:["rgb(241,238,246)","rgb(212,185,218)","rgb(201,148,199)","rgb(223,101,176)","rgb(221,28,119)","rgb(152,0,67)"],7:["rgb(241,238,246)","rgb(212,185,218)","rgb(201,148,199)","rgb(223,101,176)","rgb(231,41,138)","rgb(206,18,86)","rgb(145,0,63)"],8:["rgb(247,244,249)","rgb(231,225,239)","rgb(212,185,218)","rgb(201,148,199)","rgb(223,101,176)","rgb(231,41,138)","rgb(206,18,86)","rgb(145,0,63)"],9:["rgb(247,244,249)","rgb(231,225,239)","rgb(212,185,218)","rgb(201,148,199)","rgb(223,101,176)","rgb(231,41,138)","rgb(206,18,86)","rgb(152,0,67)","rgb(103,0,31)"]},OrRd:{3:["rgb(254,232,200)","rgb(253,187,132)","rgb(227,74,51)"],4:["rgb(254,240,217)","rgb(253,204,138)","rgb(252,141,89)","rgb(215,48,31)"],5:["rgb(254,240,217)","rgb(253,204,138)","rgb(252,141,89)","rgb(227,74,51)","rgb(179,0,0)"],6:["rgb(254,240,217)","rgb(253,212,158)","rgb(253,187,132)","rgb(252,141,89)","rgb(227,74,51)","rgb(179,0,0)"],7:["rgb(254,240,217)","rgb(253,212,158)","rgb(253,187,132)","rgb(252,141,89)","rgb(239,101,72)","rgb(215,48,31)","rgb(153,0,0)"],8:["rgb(255,247,236)","rgb(254,232,200)","rgb(253,212,158)","rgb(253,187,132)","rgb(252,141,89)","rgb(239,101,72)","rgb(215,48,31)","rgb(153,0,0)"],9:["rgb(255,247,236)","rgb(254,232,200)","rgb(253,212,158)","rgb(253,187,132)","rgb(252,141,89)","rgb(239,101,72)","rgb(215,48,31)","rgb(179,0,0)","rgb(127,0,0)"]},YlOrRd:{3:["rgb(255,237,160)","rgb(254,178,76)","rgb(240,59,32)"],4:["rgb(255,255,178)","rgb(254,204,92)","rgb(253,141,60)","rgb(227,26,28)"],5:["rgb(255,255,178)","rgb(254,204,92)","rgb(253,141,60)","rgb(240,59,32)","rgb(189,0,38)"],6:["rgb(255,255,178)","rgb(254,217,118)","rgb(254,178,76)","rgb(253,141,60)","rgb(240,59,32)","rgb(189,0,38)"],7:["rgb(255,255,178)","rgb(254,217,118)","rgb(254,178,76)","rgb(253,141,60)","rgb(252,78,42)","rgb(227,26,28)","rgb(177,0,38)"],8:["rgb(255,255,204)","rgb(255,237,160)","rgb(254,217,118)","rgb(254,178,76)","rgb(253,141,60)","rgb(252,78,42)","rgb(227,26,28)","rgb(177,0,38)"],9:["rgb(255,255,204)","rgb(255,237,160)","rgb(254,217,118)","rgb(254,178,76)","rgb(253,141,60)","rgb(252,78,42)","rgb(227,26,28)","rgb(189,0,38)","rgb(128,0,38)"]},YlOrBr:{3:["rgb(255,247,188)","rgb(254,196,79)","rgb(217,95,14)"],4:["rgb(255,255,212)","rgb(254,217,142)","rgb(254,153,41)","rgb(204,76,2)"],5:["rgb(255,255,212)","rgb(254,217,142)","rgb(254,153,41)","rgb(217,95,14)","rgb(153,52,4)"],6:["rgb(255,255,212)","rgb(254,227,145)","rgb(254,196,79)","rgb(254,153,41)","rgb(217,95,14)","rgb(153,52,4)"],7:["rgb(255,255,212)","rgb(254,227,145)","rgb(254,196,79)","rgb(254,153,41)","rgb(236,112,20)","rgb(204,76,2)","rgb(140,45,4)"],8:["rgb(255,255,229)","rgb(255,247,188)","rgb(254,227,145)","rgb(254,196,79)","rgb(254,153,41)","rgb(236,112,20)","rgb(204,76,2)","rgb(140,45,4)"],9:["rgb(255,255,229)","rgb(255,247,188)","rgb(254,227,145)","rgb(254,196,79)","rgb(254,153,41)","rgb(236,112,20)","rgb(204,76,2)","rgb(153,52,4)","rgb(102,37,6)"]},Purples:{3:["rgb(239,237,245)","rgb(188,189,220)","rgb(117,107,177)"],4:["rgb(242,240,247)","rgb(203,201,226)","rgb(158,154,200)","rgb(106,81,163)"],5:["rgb(242,240,247)","rgb(203,201,226)","rgb(158,154,200)","rgb(117,107,177)","rgb(84,39,143)"],6:["rgb(242,240,247)","rgb(218,218,235)","rgb(188,189,220)","rgb(158,154,200)","rgb(117,107,177)","rgb(84,39,143)"],7:["rgb(242,240,247)","rgb(218,218,235)","rgb(188,189,220)","rgb(158,154,200)","rgb(128,125,186)","rgb(106,81,163)","rgb(74,20,134)"],8:["rgb(252,251,253)","rgb(239,237,245)","rgb(218,218,235)","rgb(188,189,220)","rgb(158,154,200)","rgb(128,125,186)","rgb(106,81,163)","rgb(74,20,134)"],9:["rgb(252,251,253)","rgb(239,237,245)","rgb(218,218,235)","rgb(188,189,220)","rgb(158,154,200)","rgb(128,125,186)","rgb(106,81,163)","rgb(84,39,143)","rgb(63,0,125)"]},Blues:{3:["rgb(222,235,247)","rgb(158,202,225)","rgb(49,130,189)"],4:["rgb(239,243,255)","rgb(189,215,231)","rgb(107,174,214)","rgb(33,113,181)"],5:["rgb(239,243,255)","rgb(189,215,231)","rgb(107,174,214)","rgb(49,130,189)","rgb(8,81,156)"],6:["rgb(239,243,255)","rgb(198,219,239)","rgb(158,202,225)","rgb(107,174,214)","rgb(49,130,189)","rgb(8,81,156)"],7:["rgb(239,243,255)","rgb(198,219,239)","rgb(158,202,225)","rgb(107,174,214)","rgb(66,146,198)","rgb(33,113,181)","rgb(8,69,148)"],8:["rgb(247,251,255)","rgb(222,235,247)","rgb(198,219,239)","rgb(158,202,225)","rgb(107,174,214)","rgb(66,146,198)","rgb(33,113,181)","rgb(8,69,148)"],9:["rgb(247,251,255)","rgb(222,235,247)","rgb(198,219,239)","rgb(158,202,225)","rgb(107,174,214)","rgb(66,146,198)","rgb(33,113,181)","rgb(8,81,156)","rgb(8,48,107)"]},Greens:{3:["rgb(229,245,224)","rgb(161,217,155)","rgb(49,163,84)"],4:["rgb(237,248,233)","rgb(186,228,179)","rgb(116,196,118)","rgb(35,139,69)"],5:["rgb(237,248,233)","rgb(186,228,179)","rgb(116,196,118)","rgb(49,163,84)","rgb(0,109,44)"],6:["rgb(237,248,233)","rgb(199,233,192)","rgb(161,217,155)","rgb(116,196,118)","rgb(49,163,84)","rgb(0,109,44)"],7:["rgb(237,248,233)","rgb(199,233,192)","rgb(161,217,155)","rgb(116,196,118)","rgb(65,171,93)","rgb(35,139,69)","rgb(0,90,50)"],8:["rgb(247,252,245)","rgb(229,245,224)","rgb(199,233,192)","rgb(161,217,155)","rgb(116,196,118)","rgb(65,171,93)","rgb(35,139,69)","rgb(0,90,50)"],9:["rgb(247,252,245)","rgb(229,245,224)","rgb(199,233,192)","rgb(161,217,155)","rgb(116,196,118)","rgb(65,171,93)","rgb(35,139,69)","rgb(0,109,44)","rgb(0,68,27)"]},Oranges:{3:["rgb(254,230,206)","rgb(253,174,107)","rgb(230,85,13)"],4:["rgb(254,237,222)","rgb(253,190,133)","rgb(253,141,60)","rgb(217,71,1)"],5:["rgb(254,237,222)","rgb(253,190,133)","rgb(253,141,60)","rgb(230,85,13)","rgb(166,54,3)"],6:["rgb(254,237,222)","rgb(253,208,162)","rgb(253,174,107)","rgb(253,141,60)","rgb(230,85,13)","rgb(166,54,3)"],7:["rgb(254,237,222)","rgb(253,208,162)","rgb(253,174,107)","rgb(253,141,60)","rgb(241,105,19)","rgb(217,72,1)","rgb(140,45,4)"],8:["rgb(255,245,235)","rgb(254,230,206)","rgb(253,208,162)","rgb(253,174,107)","rgb(253,141,60)","rgb(241,105,19)","rgb(217,72,1)","rgb(140,45,4)"],9:["rgb(255,245,235)","rgb(254,230,206)","rgb(253,208,162)","rgb(253,174,107)","rgb(253,141,60)","rgb(241,105,19)","rgb(217,72,1)","rgb(166,54,3)","rgb(127,39,4)"]},Reds:{3:["rgb(254,224,210)","rgb(252,146,114)","rgb(222,45,38)"],4:["rgb(254,229,217)","rgb(252,174,145)","rgb(251,106,74)","rgb(203,24,29)"],5:["rgb(254,229,217)","rgb(252,174,145)","rgb(251,106,74)","rgb(222,45,38)","rgb(165,15,21)"],6:["rgb(254,229,217)","rgb(252,187,161)","rgb(252,146,114)","rgb(251,106,74)","rgb(222,45,38)","rgb(165,15,21)"],7:["rgb(254,229,217)","rgb(252,187,161)","rgb(252,146,114)","rgb(251,106,74)","rgb(239,59,44)","rgb(203,24,29)","rgb(153,0,13)"],8:["rgb(255,245,240)","rgb(254,224,210)","rgb(252,187,161)","rgb(252,146,114)","rgb(251,106,74)","rgb(239,59,44)","rgb(203,24,29)","rgb(153,0,13)"],9:["rgb(255,245,240)","rgb(254,224,210)","rgb(252,187,161)","rgb(252,146,114)","rgb(251,106,74)","rgb(239,59,44)","rgb(203,24,29)","rgb(165,15,21)","rgb(103,0,13)"]},Greys:{3:["rgb(240,240,240)","rgb(189,189,189)","rgb(99,99,99)"],4:["rgb(247,247,247)","rgb(204,204,204)","rgb(150,150,150)","rgb(82,82,82)"],5:["rgb(247,247,247)","rgb(204,204,204)","rgb(150,150,150)","rgb(99,99,99)","rgb(37,37,37)"],6:["rgb(247,247,247)","rgb(217,217,217)","rgb(189,189,189)","rgb(150,150,150)","rgb(99,99,99)","rgb(37,37,37)"],7:["rgb(247,247,247)","rgb(217,217,217)","rgb(189,189,189)","rgb(150,150,150)","rgb(115,115,115)","rgb(82,82,82)","rgb(37,37,37)"],8:["rgb(255,255,255)","rgb(240,240,240)","rgb(217,217,217)","rgb(189,189,189)","rgb(150,150,150)","rgb(115,115,115)","rgb(82,82,82)","rgb(37,37,37)"],9:["rgb(255,255,255)","rgb(240,240,240)","rgb(217,217,217)","rgb(189,189,189)","rgb(150,150,150)","rgb(115,115,115)","rgb(82,82,82)","rgb(37,37,37)","rgb(0,0,0)"]},PuOr:{3:["rgb(241,163,64)","rgb(247,247,247)","rgb(153,142,195)"],4:["rgb(230,97,1)","rgb(253,184,99)","rgb(178,171,210)","rgb(94,60,153)"],5:["rgb(230,97,1)","rgb(253,184,99)","rgb(247,247,247)","rgb(178,171,210)","rgb(94,60,153)"],6:["rgb(179,88,6)","rgb(241,163,64)","rgb(254,224,182)","rgb(216,218,235)","rgb(153,142,195)","rgb(84,39,136)"],7:["rgb(179,88,6)","rgb(241,163,64)","rgb(254,224,182)","rgb(247,247,247)","rgb(216,218,235)","rgb(153,142,195)","rgb(84,39,136)"],8:["rgb(179,88,6)","rgb(224,130,20)","rgb(253,184,99)","rgb(254,224,182)","rgb(216,218,235)","rgb(178,171,210)","rgb(128,115,172)","rgb(84,39,136)"],9:["rgb(179,88,6)","rgb(224,130,20)","rgb(253,184,99)","rgb(254,224,182)","rgb(247,247,247)","rgb(216,218,235)","rgb(178,171,210)","rgb(128,115,172)","rgb(84,39,136)"],10:["rgb(127,59,8)","rgb(179,88,6)","rgb(224,130,20)","rgb(253,184,99)","rgb(254,224,182)","rgb(216,218,235)","rgb(178,171,210)","rgb(128,115,172)","rgb(84,39,136)","rgb(45,0,75)"],11:["rgb(127,59,8)","rgb(179,88,6)","rgb(224,130,20)","rgb(253,184,99)","rgb(254,224,182)","rgb(247,247,247)","rgb(216,218,235)","rgb(178,171,210)","rgb(128,115,172)","rgb(84,39,136)","rgb(45,0,75)"]},BrBG:{3:["rgb(216,179,101)","rgb(245,245,245)","rgb(90,180,172)"],4:["rgb(166,97,26)","rgb(223,194,125)","rgb(128,205,193)","rgb(1,133,113)"],5:["rgb(166,97,26)","rgb(223,194,125)","rgb(245,245,245)","rgb(128,205,193)","rgb(1,133,113)"],6:["rgb(140,81,10)","rgb(216,179,101)","rgb(246,232,195)","rgb(199,234,229)","rgb(90,180,172)","rgb(1,102,94)"],7:["rgb(140,81,10)","rgb(216,179,101)","rgb(246,232,195)","rgb(245,245,245)","rgb(199,234,229)","rgb(90,180,172)","rgb(1,102,94)"],8:["rgb(140,81,10)","rgb(191,129,45)","rgb(223,194,125)","rgb(246,232,195)","rgb(199,234,229)","rgb(128,205,193)","rgb(53,151,143)","rgb(1,102,94)"],9:["rgb(140,81,10)","rgb(191,129,45)","rgb(223,194,125)","rgb(246,232,195)","rgb(245,245,245)","rgb(199,234,229)","rgb(128,205,193)","rgb(53,151,143)","rgb(1,102,94)"],10:["rgb(84,48,5)","rgb(140,81,10)","rgb(191,129,45)","rgb(223,194,125)","rgb(246,232,195)","rgb(199,234,229)","rgb(128,205,193)","rgb(53,151,143)","rgb(1,102,94)","rgb(0,60,48)"],11:["rgb(84,48,5)","rgb(140,81,10)","rgb(191,129,45)","rgb(223,194,125)","rgb(246,232,195)","rgb(245,245,245)","rgb(199,234,229)","rgb(128,205,193)","rgb(53,151,143)","rgb(1,102,94)","rgb(0,60,48)"]},PRGn:{3:["rgb(175,141,195)","rgb(247,247,247)","rgb(127,191,123)"],4:["rgb(123,50,148)","rgb(194,165,207)","rgb(166,219,160)","rgb(0,136,55)"],5:["rgb(123,50,148)","rgb(194,165,207)","rgb(247,247,247)","rgb(166,219,160)","rgb(0,136,55)"],6:["rgb(118,42,131)","rgb(175,141,195)","rgb(231,212,232)","rgb(217,240,211)","rgb(127,191,123)","rgb(27,120,55)"],7:["rgb(118,42,131)","rgb(175,141,195)","rgb(231,212,232)","rgb(247,247,247)","rgb(217,240,211)","rgb(127,191,123)","rgb(27,120,55)"],8:["rgb(118,42,131)","rgb(153,112,171)","rgb(194,165,207)","rgb(231,212,232)","rgb(217,240,211)","rgb(166,219,160)","rgb(90,174,97)","rgb(27,120,55)"],9:["rgb(118,42,131)","rgb(153,112,171)","rgb(194,165,207)","rgb(231,212,232)","rgb(247,247,247)","rgb(217,240,211)","rgb(166,219,160)","rgb(90,174,97)","rgb(27,120,55)"],10:["rgb(64,0,75)","rgb(118,42,131)","rgb(153,112,171)","rgb(194,165,207)","rgb(231,212,232)","rgb(217,240,211)","rgb(166,219,160)","rgb(90,174,97)","rgb(27,120,55)","rgb(0,68,27)"],11:["rgb(64,0,75)","rgb(118,42,131)","rgb(153,112,171)","rgb(194,165,207)","rgb(231,212,232)","rgb(247,247,247)","rgb(217,240,211)","rgb(166,219,160)","rgb(90,174,97)","rgb(27,120,55)","rgb(0,68,27)"]},PiYG:{3:["rgb(233,163,201)","rgb(247,247,247)","rgb(161,215,106)"],4:["rgb(208,28,139)","rgb(241,182,218)","rgb(184,225,134)","rgb(77,172,38)"],5:["rgb(208,28,139)","rgb(241,182,218)","rgb(247,247,247)","rgb(184,225,134)","rgb(77,172,38)"],6:["rgb(197,27,125)","rgb(233,163,201)","rgb(253,224,239)","rgb(230,245,208)","rgb(161,215,106)","rgb(77,146,33)"],7:["rgb(197,27,125)","rgb(233,163,201)","rgb(253,224,239)","rgb(247,247,247)","rgb(230,245,208)","rgb(161,215,106)","rgb(77,146,33)"],8:["rgb(197,27,125)","rgb(222,119,174)","rgb(241,182,218)","rgb(253,224,239)","rgb(230,245,208)","rgb(184,225,134)","rgb(127,188,65)","rgb(77,146,33)"],9:["rgb(197,27,125)","rgb(222,119,174)","rgb(241,182,218)","rgb(253,224,239)","rgb(247,247,247)","rgb(230,245,208)","rgb(184,225,134)","rgb(127,188,65)","rgb(77,146,33)"],10:["rgb(142,1,82)","rgb(197,27,125)","rgb(222,119,174)","rgb(241,182,218)","rgb(253,224,239)","rgb(230,245,208)","rgb(184,225,134)","rgb(127,188,65)","rgb(77,146,33)","rgb(39,100,25)"],11:["rgb(142,1,82)","rgb(197,27,125)","rgb(222,119,174)","rgb(241,182,218)","rgb(253,224,239)","rgb(247,247,247)","rgb(230,245,208)","rgb(184,225,134)","rgb(127,188,65)","rgb(77,146,33)","rgb(39,100,25)"]},RdBu:{3:["rgb(239,138,98)","rgb(247,247,247)","rgb(103,169,207)"],4:["rgb(202,0,32)","rgb(244,165,130)","rgb(146,197,222)","rgb(5,113,176)"],5:["rgb(202,0,32)","rgb(244,165,130)","rgb(247,247,247)","rgb(146,197,222)","rgb(5,113,176)"],6:["rgb(178,24,43)","rgb(239,138,98)","rgb(253,219,199)","rgb(209,229,240)","rgb(103,169,207)","rgb(33,102,172)"],7:["rgb(178,24,43)","rgb(239,138,98)","rgb(253,219,199)","rgb(247,247,247)","rgb(209,229,240)","rgb(103,169,207)","rgb(33,102,172)"],8:["rgb(178,24,43)","rgb(214,96,77)","rgb(244,165,130)","rgb(253,219,199)","rgb(209,229,240)","rgb(146,197,222)","rgb(67,147,195)","rgb(33,102,172)"],9:["rgb(178,24,43)","rgb(214,96,77)","rgb(244,165,130)","rgb(253,219,199)","rgb(247,247,247)","rgb(209,229,240)","rgb(146,197,222)","rgb(67,147,195)","rgb(33,102,172)"],10:["rgb(103,0,31)","rgb(178,24,43)","rgb(214,96,77)","rgb(244,165,130)","rgb(253,219,199)","rgb(209,229,240)","rgb(146,197,222)","rgb(67,147,195)","rgb(33,102,172)","rgb(5,48,97)"],11:["rgb(103,0,31)","rgb(178,24,43)","rgb(214,96,77)","rgb(244,165,130)","rgb(253,219,199)","rgb(247,247,247)","rgb(209,229,240)","rgb(146,197,222)","rgb(67,147,195)","rgb(33,102,172)","rgb(5,48,97)"]},RdGy:{3:["rgb(239,138,98)","rgb(255,255,255)","rgb(153,153,153)"],4:["rgb(202,0,32)","rgb(244,165,130)","rgb(186,186,186)","rgb(64,64,64)"],5:["rgb(202,0,32)","rgb(244,165,130)","rgb(255,255,255)","rgb(186,186,186)","rgb(64,64,64)"],6:["rgb(178,24,43)","rgb(239,138,98)","rgb(253,219,199)","rgb(224,224,224)","rgb(153,153,153)","rgb(77,77,77)"],7:["rgb(178,24,43)","rgb(239,138,98)","rgb(253,219,199)","rgb(255,255,255)","rgb(224,224,224)","rgb(153,153,153)","rgb(77,77,77)"],8:["rgb(178,24,43)","rgb(214,96,77)","rgb(244,165,130)","rgb(253,219,199)","rgb(224,224,224)","rgb(186,186,186)","rgb(135,135,135)","rgb(77,77,77)"],9:["rgb(178,24,43)","rgb(214,96,77)","rgb(244,165,130)","rgb(253,219,199)","rgb(255,255,255)","rgb(224,224,224)","rgb(186,186,186)","rgb(135,135,135)","rgb(77,77,77)"],10:["rgb(103,0,31)","rgb(178,24,43)","rgb(214,96,77)","rgb(244,165,130)","rgb(253,219,199)","rgb(224,224,224)","rgb(186,186,186)","rgb(135,135,135)","rgb(77,77,77)","rgb(26,26,26)"],11:["rgb(103,0,31)","rgb(178,24,43)","rgb(214,96,77)","rgb(244,165,130)","rgb(253,219,199)","rgb(255,255,255)","rgb(224,224,224)","rgb(186,186,186)","rgb(135,135,135)","rgb(77,77,77)","rgb(26,26,26)"]},RdYlBu:{3:["rgb(252,141,89)","rgb(255,255,191)","rgb(145,191,219)"],4:["rgb(215,25,28)","rgb(253,174,97)","rgb(171,217,233)","rgb(44,123,182)"],5:["rgb(215,25,28)","rgb(253,174,97)","rgb(255,255,191)","rgb(171,217,233)","rgb(44,123,182)"],6:["rgb(215,48,39)","rgb(252,141,89)","rgb(254,224,144)","rgb(224,243,248)","rgb(145,191,219)","rgb(69,117,180)"],7:["rgb(215,48,39)","rgb(252,141,89)","rgb(254,224,144)","rgb(255,255,191)","rgb(224,243,248)","rgb(145,191,219)","rgb(69,117,180)"],8:["rgb(215,48,39)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,144)","rgb(224,243,248)","rgb(171,217,233)","rgb(116,173,209)","rgb(69,117,180)"],9:["rgb(215,48,39)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,144)","rgb(255,255,191)","rgb(224,243,248)","rgb(171,217,233)","rgb(116,173,209)","rgb(69,117,180)"],10:["rgb(165,0,38)","rgb(215,48,39)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,144)","rgb(224,243,248)","rgb(171,217,233)","rgb(116,173,209)","rgb(69,117,180)","rgb(49,54,149)"],11:["rgb(165,0,38)","rgb(215,48,39)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,144)","rgb(255,255,191)","rgb(224,243,248)","rgb(171,217,233)","rgb(116,173,209)","rgb(69,117,180)","rgb(49,54,149)"]},Spectral:{3:["rgb(252,141,89)","rgb(255,255,191)","rgb(153,213,148)"],4:["rgb(215,25,28)","rgb(253,174,97)","rgb(171,221,164)","rgb(43,131,186)"],5:["rgb(215,25,28)","rgb(253,174,97)","rgb(255,255,191)","rgb(171,221,164)","rgb(43,131,186)"],6:["rgb(213,62,79)","rgb(252,141,89)","rgb(254,224,139)","rgb(230,245,152)","rgb(153,213,148)","rgb(50,136,189)"],7:["rgb(213,62,79)","rgb(252,141,89)","rgb(254,224,139)","rgb(255,255,191)","rgb(230,245,152)","rgb(153,213,148)","rgb(50,136,189)"],8:["rgb(213,62,79)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,139)","rgb(230,245,152)","rgb(171,221,164)","rgb(102,194,165)","rgb(50,136,189)"],9:["rgb(213,62,79)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,139)","rgb(255,255,191)","rgb(230,245,152)","rgb(171,221,164)","rgb(102,194,165)","rgb(50,136,189)"],10:["rgb(158,1,66)","rgb(213,62,79)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,139)","rgb(230,245,152)","rgb(171,221,164)","rgb(102,194,165)","rgb(50,136,189)","rgb(94,79,162)"],11:["rgb(158,1,66)","rgb(213,62,79)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,139)","rgb(255,255,191)","rgb(230,245,152)","rgb(171,221,164)","rgb(102,194,165)","rgb(50,136,189)","rgb(94,79,162)"]},RdYlGn:{3:["rgb(252,141,89)","rgb(255,255,191)","rgb(145,207,96)"],4:["rgb(215,25,28)","rgb(253,174,97)","rgb(166,217,106)","rgb(26,150,65)"],5:["rgb(215,25,28)","rgb(253,174,97)","rgb(255,255,191)","rgb(166,217,106)","rgb(26,150,65)"],6:["rgb(215,48,39)","rgb(252,141,89)","rgb(254,224,139)","rgb(217,239,139)","rgb(145,207,96)","rgb(26,152,80)"],7:["rgb(215,48,39)","rgb(252,141,89)","rgb(254,224,139)","rgb(255,255,191)","rgb(217,239,139)","rgb(145,207,96)","rgb(26,152,80)"],8:["rgb(215,48,39)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,139)","rgb(217,239,139)","rgb(166,217,106)","rgb(102,189,99)","rgb(26,152,80)"],9:["rgb(215,48,39)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,139)","rgb(255,255,191)","rgb(217,239,139)","rgb(166,217,106)","rgb(102,189,99)","rgb(26,152,80)"],10:["rgb(165,0,38)","rgb(215,48,39)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,139)","rgb(217,239,139)","rgb(166,217,106)","rgb(102,189,99)","rgb(26,152,80)","rgb(0,104,55)"],11:["rgb(165,0,38)","rgb(215,48,39)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,139)","rgb(255,255,191)","rgb(217,239,139)","rgb(166,217,106)","rgb(102,189,99)","rgb(26,152,80)","rgb(0,104,55)"]}}; -------------------------------------------------------------------------------- /_site/js/libs/cubism.elasticsearch.js: -------------------------------------------------------------------------------- 1 | // # Cubism.ElasticSearch # 2 | // 3 | // `cubism.elasticsearch` is an extension for the 4 | // [_Cubism.js_](http://square.github.com/cubism/) visualization platform 5 | // to display statistics from the _ElasticSearch_ 6 | // ["Nodes Stats API"](http://www.elasticsearch.org/guide/reference/api/admin-cluster-nodes-stats.html). 7 | // 8 | // ## Usage ## 9 | // 10 | // Setup a Cubism context, and pass it to the `cubism.elasticsearch` function: 11 | // 12 | // var context = cubism.context(), 13 | // elasticsearch = cubism.elasticsearch(context, {host: "http://localhost:9200"}, function() {...}) 14 | // 15 | // Use the `metric` function to return a specific metric from a _ElasticSearch_ node in the callback function: 16 | // 17 | // this.metric("os.cpu.user") 18 | // 19 | // By default, metric is returned from the first node (`0`). 20 | // 21 | // You may use the node's ID or a number giving it's position: 22 | // 23 | // this.metric("os.cpu.user", "USNEtnCWQW-5oG3Gf9J8Hg") 24 | // this.metric("os.cpu.user", 0) 25 | // 26 | // You can use any valid path in the JSON returned from the _ElasticSearch_: 27 | // 28 | // var basic_metrics = [ 29 | // this.metric("os.cpu.user"), 30 | // this.metric("process.cpu.percent"), 31 | // this.metric("fs.data[0].disk_reads") 32 | // // ... 33 | // ] 34 | // 35 | // Pass the returned metrics as the `data` collection to the _horizon_ chart 36 | // (https://github.com/square/cubism/wiki/Horizon#wiki-_horizon): 37 | // 38 | // var context = cubism.context(), 39 | // elasticsearch = cubism.elasticsearch(context, {host: "http://localhost:9200"}, function() { 40 | // chart.selectAll(".horizon") 41 | // .data([elasticsearch.metric("os.cpu.user"]), function(d) { return d.toString(); }) 42 | // .enter().append("div") 43 | // .attr("class", "horizon") 44 | // .call(context.horizon()) 45 | // }); 46 | // 47 | // 48 | // Use the `metrics` function to return an array with the specified metric from all nodes in the cluster: 49 | // 50 | // this.metrics("os.cpu.user") 51 | // 52 | // To display metrics from all nodes, simply pass them as the `data` collection: 53 | // 54 | // var context = cubism.context(), 55 | // elasticsearch = cubism.elasticsearch(context, {host: "http://localhost:9200"}, function() { 56 | // chart.selectAll(".horizon") 57 | // .data(elasticsearch.metrics("os.cpu.user"), function(d) { return d.toString(); }) 58 | // .enter().append("div") 59 | // .attr("class", "horizon") 60 | // .call(context.horizon()) 61 | // chart.selectAll(".horizon") 62 | // .data(elasticsearch.metrics("process.cpu.percent"), function(d) { return d.toString(); }) 63 | // .enter().append("div") 64 | // .attr("class", "horizon") 65 | // .call(context.horizon()) 66 | // }); 67 | // 68 | // Supposed you have a convenience function to add a metric to the chart, such as, 69 | // 70 | // // Function to add new chart 71 | // // 72 | // chart.add = function(metrics) { 73 | // chart.selectAll(".horizon") 74 | // .data(metrics, function(d) { return d.toString(); }) 75 | // .enter().append("div") 76 | // .attr("class", "horizon") 77 | // .call(context.horizon()) 78 | // return chart; 79 | // }; 80 | // 81 | // then adding a new metric is simply a matter of pasing it the result of `elasticsearch.metrics()` function: 82 | // 83 | // var context = cubism.context(), 84 | // elasticsearch = cubism.elasticsearch(context, {host: "http://localhost:9200"}, function() { 85 | // chart.add( elasticsearch.metrics("os.cpu.user") ) 86 | // chart.add( elasticsearch.metrics("process.cpu.percent") ) 87 | // // ... 88 | // }); 89 | // 90 | 91 | var cubism = ('undefined' == typeof cubism) ? {} : cubism; 92 | 93 | // An ElasticSearch extension for Cubism.js 94 | // 95 | // Arguments 96 | // --------- 97 | // 98 | // * `context` -- The Cubism.js context [https://github.com/square/cubism/wiki/Context] 99 | // * `options` -- Options (eg. ElasticSearch URL) 100 | // * `callback` -- Callback to execute after loading cluster info (eg. adding charts) 101 | // 102 | // Usage 103 | // ----- 104 | // 105 | // var context = cubism.context(), 106 | // 107 | // elasticsearch = cubism.elasticsearch( 108 | // context, 109 | // 110 | // {host: "http://localhost:9200"}, 111 | // 112 | // function() { 113 | // 114 | // chart.selectAll(".horizon") 115 | // 116 | // // Overall CPU (user) 117 | // // 118 | // .data(elasticsearch.metrics("os.cpu.user"), function(d) { return d.toString(); }) 119 | // 120 | // .enter().append("div") 121 | // .attr("class", "horizon") 122 | // .call(context.horizon()) 123 | // 124 | // chart.selectAll(".horizon") 125 | // 126 | // // CPU consumed by ElasticSearch 127 | // // 128 | // .data(elasticsearch.metrics("process.cpu.percent"), function(d) { return d.toString(); }) 129 | // 130 | // .enter().append("div") 131 | // .attr("class", "horizon") 132 | // .call(context.horizon()) 133 | // 134 | // chart.selectAll(".horizon") 135 | // 136 | // // Number of documents on a specific node 137 | // // 138 | // .data([elasticsearch.metric("indices.docs.count", "USNEtnCWQW-5oG3Gf9J8Hg"]), 139 | // function(d) { return d.toString(); }) 140 | // 141 | // .enter().append("div") 142 | // .attr("class", "horizon") 143 | // .call(context.horizon()) 144 | // }); 145 | // 146 | cubism.elasticsearch = function(context, options, callback) { 147 | if (!context) throw new Error("Please pass a valid Cubism context instance as the first argument"); 148 | 149 | options = options || {} 150 | if (!options.host) options.host = "http://localhost:9200"; 151 | 152 | var source = {}, 153 | context = context, 154 | initialized_metrics = {}; 155 | 156 | source.cluster = {}; 157 | source.node_names = {}; 158 | 159 | // Returns a function, which will return data for the Cubism horizon chart callbacks. 160 | // 161 | var __cubism_metric_callback = function(node, expression) { 162 | return function(start, stop, step, callback) { 163 | var values = [], 164 | value = 0, 165 | start = +start, 166 | stop = +stop, 167 | metric_id = node.id+'-'+expression 168 | 169 | d3.json(source.url(), function(data) { 170 | if (!data) return callback(new Error("Unable to load data from ElasticSearch!")) 171 | if (!data.nodes[node.id]) return callback(new Error("Unable to find node " + node.id + "!")) 172 | 173 | var value = eval("data.nodes['"+node.id+"']." + expression) // data.nodes[].os.cpu.user 174 | // console.log(expression + ': ' + value) 175 | 176 | // Cubism.js expects a value for every "slot" based on the `start` and `stop` parameters, because 177 | // it assumes a backend such as [_Graphite_](https://github.com/square/cubism/wiki/Graphite), 178 | // which is able to return values stored over time. 179 | // 180 | // In _ElasticSearch_, we don't have any data stored: we poll the API repeatedly. 181 | // 182 | // On first call, Cubism.js calls the metric callback function with a large `start` and `stop` gap, 183 | // based on the `step` and `size` values of your chart. This would spoil the chart with a useless 184 | // "thick colored line". 185 | // 186 | // So: if we have already initialized this metric, push the same value to all the "slots", 187 | // because this is what Cubism.js expects... 188 | if (initialized_metrics[metric_id]) { 189 | while (start < stop) { 190 | start += step; 191 | values.push(value); 192 | } 193 | } 194 | // ... otherwise mark this metric as initialized and fill the empty / "historical" slots with empty values. 195 | else { 196 | initialized_metrics[metric_id] = true; 197 | while (start < (stop - step)) { 198 | start += step; 199 | values.push(NaN); 200 | } 201 | values.push(value); 202 | } 203 | 204 | callback(null, values) 205 | }); 206 | } 207 | } 208 | 209 | // Load information about ElasticSearch nodes from the Nodes Info API 210 | // [http://www.elasticsearch.org/guide/reference/api/admin-cluster-nodes-info.html] 211 | // 212 | d3.json(options.host + "/_nodes", function(cluster) { 213 | source.cluster = cluster 214 | source.node_names = d3.keys(cluster.nodes) 215 | 216 | // Returns a metric for specific node 217 | // 218 | // Arguments 219 | // --------- 220 | // 221 | // * `expression` -- A valid path in the ElasticSearch JSON response (eg. `os.cpu.user`) 222 | // * `n` -- A node specification. Can be either node ID (eg. `USNEtnCWQW-5oG3Gf9J8Hg`), 223 | // or a number giving position in response (eg. `0`) 224 | // 225 | // For usage, see documentation for `cubism.elasticsearch` 226 | // 227 | source.metric = function(expression, n) { 228 | var n = n || 0, 229 | node_id = ("number" == typeof n) ? source.node_names[n] : n, 230 | node = source.cluster.nodes[node_id]; 231 | 232 | var metric = context.metric(__cubism_metric_callback(node, expression), expression + " [" + node.name + "]"); 233 | 234 | return metric; 235 | }; 236 | 237 | // Returns a metric across all nodes 238 | // 239 | // Arguments 240 | // --------- 241 | // 242 | // * `expression` -- A valid path in the ElasticSearch JSON response (eg. `os.cpu.user`) 243 | // 244 | // For usage, see documentation for `cubism.elasticsearch` 245 | // 246 | source.metrics = function(expression) { 247 | var metrics = [], 248 | ordered_nodes = []; 249 | for ( var n in source.cluster.nodes ) 250 | { var o = source.cluster.nodes[n]; o.id = n; ordered_nodes.push(o) } 251 | 252 | ordered_nodes = ordered_nodes.sort(function(a,b) { 253 | if (a.name < b.name) return -1; 254 | if (a.name > b.name) return 1; 255 | return 0; 256 | }); 257 | 258 | ordered_nodes.forEach( function(node) { 259 | var metric = context.metric(__cubism_metric_callback(node, expression), expression + " [" + node.name + "]"); 260 | metrics.push(metric) 261 | }) 262 | 263 | return metrics; 264 | }; 265 | 266 | callback.call(source) 267 | }) 268 | 269 | source.toString = function() { return options.host }; 270 | source.url = function() { return options.host + "/_nodes/stats?all" }; 271 | 272 | return source; 273 | }; 274 | -------------------------------------------------------------------------------- /_site/js/libs/cubism.v1.js: -------------------------------------------------------------------------------- 1 | (function(exports){ 2 | var cubism = exports.cubism = {version: "1.0.1"}; 3 | var cubism_id = 0; 4 | function cubism_identity(d) { return d; } 5 | cubism.option = function(name, defaultValue) { 6 | var values = cubism.options(name); 7 | return values.length ? values[0] : defaultValue; 8 | }; 9 | 10 | cubism.options = function(name, defaultValues) { 11 | var options = location.search.substring(1).split("&"), 12 | values = [], 13 | i = -1, 14 | n = options.length, 15 | o; 16 | while (++i < n) { 17 | if ((o = options[i].split("="))[0] == name) { 18 | values.push(decodeURIComponent(o[1])); 19 | } 20 | } 21 | return values.length || arguments.length < 2 ? values : defaultValues; 22 | }; 23 | cubism.context = function() { 24 | var context = new cubism_context, 25 | step = 1e4, // ten seconds, in milliseconds 26 | size = 1440, // four hours at ten seconds, in pixels 27 | start0, stop0, // the start and stop for the previous change event 28 | start1, stop1, // the start and stop for the next prepare event 29 | serverDelay = 5e3, 30 | clientDelay = 5e3, 31 | event = d3.dispatch("prepare", "beforechange", "change", "focus"), 32 | scale = context.scale = d3.time.scale().range([0, size]), 33 | timeout, 34 | focus; 35 | 36 | function update() { 37 | var now = Date.now(); 38 | stop0 = new Date(Math.floor((now - serverDelay - clientDelay) / step) * step); 39 | start0 = new Date(stop0 - size * step); 40 | stop1 = new Date(Math.floor((now - serverDelay) / step) * step); 41 | start1 = new Date(stop1 - size * step); 42 | scale.domain([start0, stop0]); 43 | return context; 44 | } 45 | 46 | context.start = function() { 47 | if (timeout) clearTimeout(timeout); 48 | var delay = +stop1 + serverDelay - Date.now(); 49 | 50 | // If we're too late for the first prepare event, skip it. 51 | if (delay < clientDelay) delay += step; 52 | 53 | timeout = setTimeout(function prepare() { 54 | stop1 = new Date(Math.floor((Date.now() - serverDelay) / step) * step); 55 | start1 = new Date(stop1 - size * step); 56 | event.prepare.call(context, start1, stop1); 57 | 58 | setTimeout(function() { 59 | scale.domain([start0 = start1, stop0 = stop1]); 60 | event.beforechange.call(context, start1, stop1); 61 | event.change.call(context, start1, stop1); 62 | event.focus.call(context, focus); 63 | }, clientDelay); 64 | 65 | timeout = setTimeout(prepare, step); 66 | }, delay); 67 | return context; 68 | }; 69 | 70 | context.stop = function() { 71 | timeout = clearTimeout(timeout); 72 | return context; 73 | }; 74 | 75 | timeout = setTimeout(context.start, 10); 76 | 77 | // Set or get the step interval in milliseconds. 78 | // Defaults to ten seconds. 79 | context.step = function(_) { 80 | if (!arguments.length) return step; 81 | step = +_; 82 | return update(); 83 | }; 84 | 85 | // Set or get the context size (the count of metric values). 86 | // Defaults to 1440 (four hours at ten seconds). 87 | context.size = function(_) { 88 | if (!arguments.length) return size; 89 | scale.range([0, size = +_]); 90 | return update(); 91 | }; 92 | 93 | // The server delay is the amount of time we wait for the server to compute a 94 | // metric. This delay may result from clock skew or from delays collecting 95 | // metrics from various hosts. Defaults to 4 seconds. 96 | context.serverDelay = function(_) { 97 | if (!arguments.length) return serverDelay; 98 | serverDelay = +_; 99 | return update(); 100 | }; 101 | 102 | // The client delay is the amount of additional time we wait to fetch those 103 | // metrics from the server. The client and server delay combined represent the 104 | // age of the most recent displayed metric. Defaults to 1 second. 105 | context.clientDelay = function(_) { 106 | if (!arguments.length) return clientDelay; 107 | clientDelay = +_; 108 | return update(); 109 | }; 110 | 111 | // Sets the focus to the specified index, and dispatches a "focus" event. 112 | context.focus = function(i) { 113 | event.focus.call(context, focus = i); 114 | return context; 115 | }; 116 | 117 | // Add, remove or get listeners for events. 118 | context.on = function(type, listener) { 119 | if (arguments.length < 2) return event.on(type); 120 | 121 | event.on(type, listener); 122 | 123 | // Notify the listener of the current start and stop time, as appropriate. 124 | // This way, metrics can make requests for data immediately, 125 | // and likewise the axis can display itself synchronously. 126 | if (listener != null) { 127 | if (/^prepare(\.|$)/.test(type)) listener.call(context, start1, stop1); 128 | if (/^beforechange(\.|$)/.test(type)) listener.call(context, start0, stop0); 129 | if (/^change(\.|$)/.test(type)) listener.call(context, start0, stop0); 130 | if (/^focus(\.|$)/.test(type)) listener.call(context, focus); 131 | } 132 | 133 | return context; 134 | }; 135 | 136 | d3.select(window).on("keydown.context-" + ++cubism_id, function() { 137 | switch (!d3.event.metaKey && d3.event.keyCode) { 138 | case 37: // left 139 | if (focus == null) focus = size - 1; 140 | if (focus > 0) context.focus(--focus); 141 | break; 142 | case 39: // right 143 | if (focus == null) focus = size - 2; 144 | if (focus < size - 1) context.focus(++focus); 145 | break; 146 | default: return; 147 | } 148 | d3.event.preventDefault(); 149 | }); 150 | 151 | return update(); 152 | }; 153 | 154 | function cubism_context() {} 155 | 156 | var cubism_contextPrototype = cubism_context.prototype; 157 | 158 | cubism_contextPrototype.constant = function(value) { 159 | return new cubism_metricConstant(this, +value); 160 | }; 161 | cubism_contextPrototype.cube = function(host) { 162 | if (!arguments.length) host = ""; 163 | var source = {}, 164 | context = this; 165 | 166 | source.metric = function(expression) { 167 | return context.metric(function(start, stop, step, callback) { 168 | d3.json(host + "/1.0/metric" 169 | + "?expression=" + encodeURIComponent(expression) 170 | + "&start=" + cubism_cubeFormatDate(start) 171 | + "&stop=" + cubism_cubeFormatDate(stop) 172 | + "&step=" + step, function(data) { 173 | if (!data) return callback(new Error("unable to load data")); 174 | callback(null, data.map(function(d) { return d.value; })); 175 | }); 176 | }, expression += ""); 177 | }; 178 | 179 | // Returns the Cube host. 180 | source.toString = function() { 181 | return host; 182 | }; 183 | 184 | return source; 185 | }; 186 | 187 | var cubism_cubeFormatDate = d3.time.format.iso; 188 | cubism_contextPrototype.graphite = function(host) { 189 | if (!arguments.length) host = ""; 190 | var source = {}, 191 | context = this; 192 | 193 | source.metric = function(expression) { 194 | return context.metric(function(start, stop, step, callback) { 195 | d3.text(host + "/render?format=raw" 196 | + "&target=" + encodeURIComponent("alias(" + expression + ",'')") 197 | + "&from=" + cubism_graphiteFormatDate(start - 2 * step) // off-by-two? 198 | + "&until=" + cubism_graphiteFormatDate(stop - 1000), function(text) { 199 | if (!text) return callback(new Error("unable to load data")); 200 | callback(null, cubism_graphiteParse(text)); 201 | }); 202 | }, expression += ""); 203 | }; 204 | 205 | source.find = function(pattern, callback) { 206 | d3.json(host + "/metrics/find?format=completer" 207 | + "&query=" + encodeURIComponent(pattern), function(result) { 208 | if (!result) return callback(new Error("unable to find metrics")); 209 | callback(null, result.metrics.map(function(d) { return d.path; })); 210 | }); 211 | }; 212 | 213 | // Returns the graphite host. 214 | source.toString = function() { 215 | return host; 216 | }; 217 | 218 | return source; 219 | }; 220 | 221 | // Graphite understands seconds since UNIX epoch. 222 | function cubism_graphiteFormatDate(time) { 223 | return Math.floor(time / 1000); 224 | } 225 | 226 | // Helper method for parsing graphite's raw format. 227 | function cubism_graphiteParse(text) { 228 | var i = text.indexOf("|"), 229 | meta = text.substring(0, i), 230 | c = meta.lastIndexOf(","), 231 | b = meta.lastIndexOf(",", c - 1), 232 | a = meta.lastIndexOf(",", b - 1), 233 | start = meta.substring(a + 1, b) * 1000, 234 | step = meta.substring(c + 1) * 1000; 235 | return text 236 | .substring(i + 1) 237 | .split(",") 238 | .slice(1) // the first value is always None? 239 | .map(function(d) { return +d; }); 240 | } 241 | function cubism_metric(context) { 242 | if (!(context instanceof cubism_context)) throw new Error("invalid context"); 243 | this.context = context; 244 | } 245 | 246 | var cubism_metricPrototype = cubism_metric.prototype; 247 | 248 | cubism_metricPrototype.valueAt = function() { 249 | return NaN; 250 | }; 251 | 252 | cubism_metricPrototype.alias = function(name) { 253 | this.toString = function() { return name; }; 254 | return this; 255 | }; 256 | 257 | cubism_metricPrototype.extent = function() { 258 | var i = 0, 259 | n = this.context.size(), 260 | value, 261 | min = Infinity, 262 | max = -Infinity; 263 | while (++i < n) { 264 | value = this.valueAt(i); 265 | if (value < min) min = value; 266 | if (value > max) max = value; 267 | } 268 | return [min, max]; 269 | }; 270 | 271 | cubism_metricPrototype.on = function(type, listener) { 272 | return arguments.length < 2 ? null : this; 273 | }; 274 | 275 | cubism_metricPrototype.shift = function() { 276 | return this; 277 | }; 278 | 279 | cubism_metricPrototype.on = function() { 280 | return arguments.length < 2 ? null : this; 281 | }; 282 | 283 | cubism_contextPrototype.metric = function(request, name) { 284 | var context = this, 285 | metric = new cubism_metric(context), 286 | id = ".metric-" + ++cubism_id, 287 | start = -Infinity, 288 | stop, 289 | step = context.step(), 290 | size = context.size(), 291 | values = [], 292 | event = d3.dispatch("change"), 293 | listening = 0, 294 | fetching; 295 | 296 | // Prefetch new data into a temporary array. 297 | function prepare(start1, stop) { 298 | var steps = Math.min(size, Math.round((start1 - start) / step)); 299 | if (!steps || fetching) return; // already fetched, or fetching! 300 | fetching = true; 301 | steps = Math.min(size, steps + cubism_metricOverlap); 302 | var start0 = new Date(stop - steps * step); 303 | request(start0, stop, step, function(error, data) { 304 | fetching = false; 305 | if (error) return console.warn(error); 306 | var i = isFinite(start) ? Math.round((start0 - start) / step) : 0; 307 | for (var j = 0, m = data.length; j < m; ++j) values[j + i] = data[j]; 308 | event.change.call(metric, start, stop); 309 | }); 310 | } 311 | 312 | // When the context changes, switch to the new data, ready-or-not! 313 | function beforechange(start1, stop1) { 314 | if (!isFinite(start)) start = start1; 315 | values.splice(0, Math.max(0, Math.min(size, Math.round((start1 - start) / step)))); 316 | start = start1; 317 | stop = stop1; 318 | } 319 | 320 | // 321 | metric.valueAt = function(i) { 322 | return values[i]; 323 | }; 324 | 325 | // 326 | metric.shift = function(offset) { 327 | return context.metric(cubism_metricShift(request, +offset)); 328 | }; 329 | 330 | // 331 | metric.on = function(type, listener) { 332 | if (!arguments.length) return event.on(type); 333 | 334 | // If there are no listeners, then stop listening to the context, 335 | // and avoid unnecessary fetches. 336 | if (listener == null) { 337 | if (event.on(type) != null && --listening == 0) { 338 | context.on("prepare" + id, null).on("beforechange" + id, null); 339 | } 340 | } else { 341 | if (event.on(type) == null && ++listening == 1) { 342 | context.on("prepare" + id, prepare).on("beforechange" + id, beforechange); 343 | } 344 | } 345 | 346 | event.on(type, listener); 347 | 348 | // Notify the listener of the current start and stop time, as appropriate. 349 | // This way, charts can display synchronous metrics immediately. 350 | if (listener != null) { 351 | if (/^change(\.|$)/.test(type)) listener.call(context, start, stop); 352 | } 353 | 354 | return metric; 355 | }; 356 | 357 | // 358 | if (arguments.length > 1) metric.toString = function() { 359 | return name; 360 | }; 361 | 362 | return metric; 363 | }; 364 | 365 | // Number of metric to refetch each period, in case of lag. 366 | var cubism_metricOverlap = 6; 367 | 368 | // Wraps the specified request implementation, and shifts time by the given offset. 369 | function cubism_metricShift(request, offset) { 370 | return function(start, stop, step, callback) { 371 | request(new Date(+start + offset), new Date(+stop + offset), step, callback); 372 | }; 373 | } 374 | function cubism_metricConstant(context, value) { 375 | cubism_metric.call(this, context); 376 | value = +value; 377 | var name = value + ""; 378 | this.valueOf = function() { return value; }; 379 | this.toString = function() { return name; }; 380 | } 381 | 382 | var cubism_metricConstantPrototype = cubism_metricConstant.prototype = Object.create(cubism_metric.prototype); 383 | 384 | cubism_metricConstantPrototype.valueAt = function() { 385 | return +this; 386 | }; 387 | 388 | cubism_metricConstantPrototype.extent = function() { 389 | return [+this, +this]; 390 | }; 391 | function cubism_metricOperator(name, operate) { 392 | 393 | function cubism_metricOperator(left, right) { 394 | if (!(right instanceof cubism_metric)) right = new cubism_metricConstant(left.context, right); 395 | else if (left.context !== right.context) throw new Error("mismatch context"); 396 | cubism_metric.call(this, left.context); 397 | this.left = left; 398 | this.right = right; 399 | this.toString = function() { return left + " " + name + " " + right; }; 400 | } 401 | 402 | var cubism_metricOperatorPrototype = cubism_metricOperator.prototype = Object.create(cubism_metric.prototype); 403 | 404 | cubism_metricOperatorPrototype.valueAt = function(i) { 405 | return operate(this.left.valueAt(i), this.right.valueAt(i)); 406 | }; 407 | 408 | cubism_metricOperatorPrototype.shift = function(offset) { 409 | return new cubism_metricOperator(this.left.shift(offset), this.right.shift(offset)); 410 | }; 411 | 412 | cubism_metricOperatorPrototype.on = function(type, listener) { 413 | if (arguments.length < 2) return this.left.on(type); 414 | this.left.on(type, listener); 415 | this.right.on(type, listener); 416 | return this; 417 | }; 418 | 419 | return function(right) { 420 | return new cubism_metricOperator(this, right); 421 | }; 422 | } 423 | 424 | cubism_metricPrototype.add = cubism_metricOperator("+", function(left, right) { 425 | return left + right; 426 | }); 427 | 428 | cubism_metricPrototype.subtract = cubism_metricOperator("-", function(left, right) { 429 | return left - right; 430 | }); 431 | 432 | cubism_metricPrototype.multiply = cubism_metricOperator("*", function(left, right) { 433 | return left * right; 434 | }); 435 | 436 | cubism_metricPrototype.divide = cubism_metricOperator("/", function(left, right) { 437 | return left / right; 438 | }); 439 | cubism_contextPrototype.horizon = function() { 440 | var context = this, 441 | mode = "offset", 442 | buffer = document.createElement("canvas"), 443 | width = buffer.width = context.size(), 444 | height = buffer.height = 30, 445 | scale = d3.scale.linear().interpolate(d3.interpolateRound), 446 | metric = cubism_identity, 447 | extent = null, 448 | title = cubism_identity, 449 | format = d3.format(".2s"), 450 | colors = ["#08519c","#3182bd","#6baed6","#bdd7e7","#bae4b3","#74c476","#31a354","#006d2c"]; 451 | 452 | function horizon(selection) { 453 | 454 | selection 455 | .on("mousemove.horizon", function() { context.focus(d3.mouse(this)[0]); }) 456 | .on("mouseout.horizon", function() { context.focus(null); }); 457 | 458 | selection.append("canvas") 459 | .attr("width", width) 460 | .attr("height", height); 461 | 462 | selection.append("span") 463 | .attr("class", "title") 464 | .text(title); 465 | 466 | selection.append("span") 467 | .attr("class", "value"); 468 | 469 | selection.each(function(d, i) { 470 | var that = this, 471 | id = ++cubism_id, 472 | metric_ = typeof metric === "function" ? metric.call(that, d, i) : metric, 473 | colors_ = typeof colors === "function" ? colors.call(that, d, i) : colors, 474 | extent_ = typeof extent === "function" ? extent.call(that, d, i) : extent, 475 | start = -Infinity, 476 | step = context.step(), 477 | canvas = d3.select(that).select("canvas"), 478 | span = d3.select(that).select(".value"), 479 | max_, 480 | m = colors_.length >> 1, 481 | ready; 482 | 483 | canvas.datum({id: id, metric: metric_}); 484 | canvas = canvas.node().getContext("2d"); 485 | 486 | function change(start1, stop) { 487 | canvas.save(); 488 | 489 | // compute the new extent and ready flag 490 | var extent = metric_.extent(); 491 | ready = extent.every(isFinite); 492 | if (extent_ != null) extent = extent_; 493 | 494 | // if this is an update (with no extent change), copy old values! 495 | var i0 = 0, max = Math.max(-extent[0], extent[1]); 496 | if (this === context) { 497 | if (max == max_) { 498 | i0 = width - cubism_metricOverlap; 499 | var dx = (start1 - start) / step; 500 | if (dx < width) { 501 | var canvas0 = buffer.getContext("2d"); 502 | canvas0.clearRect(0, 0, width, height); 503 | canvas0.drawImage(canvas.canvas, dx, 0, width - dx, height, 0, 0, width - dx, height); 504 | canvas.clearRect(0, 0, width, height); 505 | canvas.drawImage(canvas0.canvas, 0, 0); 506 | } 507 | } 508 | start = start1; 509 | } 510 | 511 | // update the domain 512 | scale.domain([0, max_ = max]); 513 | 514 | // clear for the new data 515 | canvas.clearRect(i0, 0, width - i0, height); 516 | 517 | // record whether there are negative values to display 518 | var negative; 519 | 520 | // positive bands 521 | for (var j = 0; j < m; ++j) { 522 | canvas.fillStyle = colors_[m + j]; 523 | 524 | // Adjust the range based on the current band index. 525 | var y0 = (j - m + 1) * height; 526 | scale.range([m * height + y0, y0]); 527 | y0 = scale(0); 528 | 529 | for (var i = i0, n = width, y1; i < n; ++i) { 530 | y1 = metric_.valueAt(i); 531 | if (y1 <= 0) { negative = true; continue; } 532 | canvas.fillRect(i, y1 = scale(y1), 1, y0 - y1); 533 | } 534 | } 535 | 536 | if (negative) { 537 | // enable offset mode 538 | if (mode === "offset") { 539 | canvas.translate(0, height); 540 | canvas.scale(1, -1); 541 | } 542 | 543 | // negative bands 544 | for (var j = 0; j < m; ++j) { 545 | canvas.fillStyle = colors_[m - 1 - j]; 546 | 547 | // Adjust the range based on the current band index. 548 | var y0 = (j - m + 1) * height; 549 | scale.range([m * height + y0, y0]); 550 | y0 = scale(0); 551 | 552 | for (var i = i0, n = width, y1; i < n; ++i) { 553 | y1 = metric_.valueAt(i); 554 | if (y1 >= 0) continue; 555 | canvas.fillRect(i, scale(-y1), 1, y0 - scale(-y1)); 556 | } 557 | } 558 | } 559 | 560 | canvas.restore(); 561 | } 562 | 563 | function focus(i) { 564 | if (i == null) i = width - 1; 565 | var value = metric_.valueAt(i); 566 | span.datum(value).text(isNaN(value) ? null : format); 567 | } 568 | 569 | // Update the chart when the context changes. 570 | context.on("change.horizon-" + id, change); 571 | context.on("focus.horizon-" + id, focus); 572 | 573 | // Display the first metric change immediately, 574 | // but defer subsequent updates to the canvas change. 575 | // Note that someone still needs to listen to the metric, 576 | // so that it continues to update automatically. 577 | metric_.on("change.horizon-" + id, function(start, stop) { 578 | change(start, stop), focus(); 579 | if (ready) metric_.on("change.horizon-" + id, cubism_identity); 580 | }); 581 | }); 582 | } 583 | 584 | horizon.remove = function(selection) { 585 | 586 | selection 587 | .on("mousemove.horizon", null) 588 | .on("mouseout.horizon", null); 589 | 590 | selection.selectAll("canvas") 591 | .each(remove) 592 | .remove(); 593 | 594 | selection.selectAll(".title,.value") 595 | .remove(); 596 | 597 | function remove(d) { 598 | d.metric.on("change.horizon-" + d.id, null); 599 | context.on("change.horizon-" + d.id, null); 600 | context.on("focus.horizon-" + d.id, null); 601 | } 602 | }; 603 | 604 | horizon.mode = function(_) { 605 | if (!arguments.length) return mode; 606 | mode = _ + ""; 607 | return horizon; 608 | }; 609 | 610 | horizon.height = function(_) { 611 | if (!arguments.length) return height; 612 | buffer.height = height = +_; 613 | return horizon; 614 | }; 615 | 616 | horizon.metric = function(_) { 617 | if (!arguments.length) return metric; 618 | metric = _; 619 | return horizon; 620 | }; 621 | 622 | horizon.scale = function(_) { 623 | if (!arguments.length) return scale; 624 | scale = _; 625 | return horizon; 626 | }; 627 | 628 | horizon.extent = function(_) { 629 | if (!arguments.length) return extent; 630 | extent = _; 631 | return horizon; 632 | }; 633 | 634 | horizon.title = function(_) { 635 | if (!arguments.length) return title; 636 | title = _; 637 | return horizon; 638 | }; 639 | 640 | horizon.format = function(_) { 641 | if (!arguments.length) return format; 642 | format = _; 643 | return horizon; 644 | }; 645 | 646 | horizon.colors = function(_) { 647 | if (!arguments.length) return colors; 648 | colors = _; 649 | return horizon; 650 | }; 651 | 652 | return horizon; 653 | }; 654 | cubism_contextPrototype.comparison = function() { 655 | var context = this, 656 | width = context.size(), 657 | height = 120, 658 | scale = d3.scale.linear().interpolate(d3.interpolateRound), 659 | primary = function(d) { return d[0]; }, 660 | secondary = function(d) { return d[1]; }, 661 | extent = null, 662 | title = cubism_identity, 663 | formatPrimary = cubism_comparisonPrimaryFormat, 664 | formatChange = cubism_comparisonChangeFormat, 665 | colors = ["#9ecae1", "#225b84", "#a1d99b", "#22723a"], 666 | strokeWidth = 1.5; 667 | 668 | function comparison(selection) { 669 | 670 | selection 671 | .on("mousemove.comparison", function() { context.focus(d3.mouse(this)[0]); }) 672 | .on("mouseout.comparison", function() { context.focus(null); }); 673 | 674 | selection.append("canvas") 675 | .attr("width", width) 676 | .attr("height", height); 677 | 678 | selection.append("span") 679 | .attr("class", "title") 680 | .text(title); 681 | 682 | selection.append("span") 683 | .attr("class", "value primary"); 684 | 685 | selection.append("span") 686 | .attr("class", "value change"); 687 | 688 | selection.each(function(d, i) { 689 | var that = this, 690 | id = ++cubism_id, 691 | primary_ = typeof primary === "function" ? primary.call(that, d, i) : primary, 692 | secondary_ = typeof secondary === "function" ? secondary.call(that, d, i) : secondary, 693 | extent_ = typeof extent === "function" ? extent.call(that, d, i) : extent, 694 | div = d3.select(that), 695 | canvas = div.select("canvas"), 696 | spanPrimary = div.select(".value.primary"), 697 | spanChange = div.select(".value.change"), 698 | ready; 699 | 700 | canvas.datum({id: id, primary: primary_, secondary: secondary_}); 701 | canvas = canvas.node().getContext("2d"); 702 | 703 | function change(start, stop) { 704 | canvas.save(); 705 | canvas.clearRect(0, 0, width, height); 706 | 707 | // update the scale 708 | var primaryExtent = primary_.extent(), 709 | secondaryExtent = secondary_.extent(), 710 | extent = extent_ == null ? primaryExtent : extent_; 711 | scale.domain(extent).range([height, 0]); 712 | ready = primaryExtent.concat(secondaryExtent).every(isFinite); 713 | 714 | // consistent overplotting 715 | var round = start / context.step() & 1 716 | ? cubism_comparisonRoundOdd 717 | : cubism_comparisonRoundEven; 718 | 719 | // positive changes 720 | canvas.fillStyle = colors[2]; 721 | for (var i = 0, n = width; i < n; ++i) { 722 | var y0 = scale(primary_.valueAt(i)), 723 | y1 = scale(secondary_.valueAt(i)); 724 | if (y0 < y1) canvas.fillRect(round(i), y0, 1, y1 - y0); 725 | } 726 | 727 | // negative changes 728 | canvas.fillStyle = colors[0]; 729 | for (i = 0; i < n; ++i) { 730 | var y0 = scale(primary_.valueAt(i)), 731 | y1 = scale(secondary_.valueAt(i)); 732 | if (y0 > y1) canvas.fillRect(round(i), y1, 1, y0 - y1); 733 | } 734 | 735 | // positive values 736 | canvas.fillStyle = colors[3]; 737 | for (i = 0; i < n; ++i) { 738 | var y0 = scale(primary_.valueAt(i)), 739 | y1 = scale(secondary_.valueAt(i)); 740 | if (y0 <= y1) canvas.fillRect(round(i), y0, 1, strokeWidth); 741 | } 742 | 743 | // negative values 744 | canvas.fillStyle = colors[1]; 745 | for (i = 0; i < n; ++i) { 746 | var y0 = scale(primary_.valueAt(i)), 747 | y1 = scale(secondary_.valueAt(i)); 748 | if (y0 > y1) canvas.fillRect(round(i), y0 - strokeWidth, 1, strokeWidth); 749 | } 750 | 751 | canvas.restore(); 752 | } 753 | 754 | function focus(i) { 755 | if (i == null) i = width - 1; 756 | var valuePrimary = primary_.valueAt(i), 757 | valueSecondary = secondary_.valueAt(i), 758 | valueChange = (valuePrimary - valueSecondary) / valueSecondary; 759 | 760 | spanPrimary 761 | .datum(valuePrimary) 762 | .text(isNaN(valuePrimary) ? null : formatPrimary); 763 | 764 | spanChange 765 | .datum(valueChange) 766 | .text(isNaN(valueChange) ? null : formatChange) 767 | .attr("class", "value change " + (valueChange > 0 ? "positive" : valueChange < 0 ? "negative" : "")); 768 | } 769 | 770 | // Display the first primary change immediately, 771 | // but defer subsequent updates to the context change. 772 | // Note that someone still needs to listen to the metric, 773 | // so that it continues to update automatically. 774 | primary_.on("change.comparison-" + id, firstChange); 775 | secondary_.on("change.comparison-" + id, firstChange); 776 | function firstChange(start, stop) { 777 | change(start, stop), focus(); 778 | if (ready) { 779 | primary_.on("change.comparison-" + id, cubism_identity); 780 | secondary_.on("change.comparison-" + id, cubism_identity); 781 | } 782 | } 783 | 784 | // Update the chart when the context changes. 785 | context.on("change.comparison-" + id, change); 786 | context.on("focus.comparison-" + id, focus); 787 | }); 788 | } 789 | 790 | comparison.remove = function(selection) { 791 | 792 | selection 793 | .on("mousemove.comparison", null) 794 | .on("mouseout.comparison", null); 795 | 796 | selection.selectAll("canvas") 797 | .each(remove) 798 | .remove(); 799 | 800 | selection.selectAll(".title,.value") 801 | .remove(); 802 | 803 | function remove(d) { 804 | d.primary.on("change.comparison-" + d.id, null); 805 | d.secondary.on("change.comparison-" + d.id, null); 806 | context.on("change.comparison-" + d.id, null); 807 | context.on("focus.comparison-" + d.id, null); 808 | } 809 | }; 810 | 811 | comparison.height = function(_) { 812 | if (!arguments.length) return height; 813 | height = +_; 814 | return comparison; 815 | }; 816 | 817 | comparison.primary = function(_) { 818 | if (!arguments.length) return primary; 819 | primary = _; 820 | return comparison; 821 | }; 822 | 823 | comparison.secondary = function(_) { 824 | if (!arguments.length) return secondary; 825 | secondary = _; 826 | return comparison; 827 | }; 828 | 829 | comparison.scale = function(_) { 830 | if (!arguments.length) return scale; 831 | scale = _; 832 | return comparison; 833 | }; 834 | 835 | comparison.extent = function(_) { 836 | if (!arguments.length) return extent; 837 | extent = _; 838 | return comparison; 839 | }; 840 | 841 | comparison.title = function(_) { 842 | if (!arguments.length) return title; 843 | title = _; 844 | return comparison; 845 | }; 846 | 847 | comparison.formatPrimary = function(_) { 848 | if (!arguments.length) return formatPrimary; 849 | formatPrimary = _; 850 | return comparison; 851 | }; 852 | 853 | comparison.formatChange = function(_) { 854 | if (!arguments.length) return formatChange; 855 | formatChange = _; 856 | return comparison; 857 | }; 858 | 859 | comparison.colors = function(_) { 860 | if (!arguments.length) return colors; 861 | colors = _; 862 | return comparison; 863 | }; 864 | 865 | comparison.strokeWidth = function(_) { 866 | if (!arguments.length) return strokeWidth; 867 | strokeWidth = _; 868 | return comparison; 869 | }; 870 | 871 | return comparison; 872 | }; 873 | 874 | var cubism_comparisonPrimaryFormat = d3.format(".2s"), 875 | cubism_comparisonChangeFormat = d3.format("+.0%"); 876 | 877 | function cubism_comparisonRoundEven(i) { 878 | return i & 0xfffffe; 879 | } 880 | 881 | function cubism_comparisonRoundOdd(i) { 882 | return ((i + 1) & 0xfffffe) - 1; 883 | } 884 | cubism_contextPrototype.axis = function() { 885 | var context = this, 886 | scale = context.scale, 887 | axis_ = d3.svg.axis().scale(scale), 888 | format = context.step() < 6e4 ? cubism_axisFormatSeconds : cubism_axisFormatMinutes; 889 | 890 | function axis(selection) { 891 | var id = ++cubism_id, 892 | tick; 893 | 894 | var g = selection.append("svg") 895 | .datum({id: id}) 896 | .attr("width", context.size()) 897 | .attr("height", Math.max(28, -axis.tickSize())) 898 | .append("g") 899 | .attr("transform", "translate(0," + (axis_.orient() === "top" ? 27 : 4) + ")") 900 | .call(axis_); 901 | 902 | context.on("change.axis-" + id, function() { 903 | g.call(axis_); 904 | if (!tick) tick = cloneTick(); 905 | }); 906 | 907 | context.on("focus.axis-" + id, function(i) { 908 | if (tick) { 909 | if (i == null) { 910 | tick.style("display", "none"); 911 | g.selectAll("text").style("fill-opacity", null); 912 | } else { 913 | tick.style("display", null).attr("x", i).text(format(scale.invert(i))); 914 | var dx = tick.node().getComputedTextLength() + 6; 915 | g.selectAll("text").style("fill-opacity", function(d) { return Math.abs(scale(d) - i) < dx ? 0 : 1; }); 916 | } 917 | } 918 | }); 919 | 920 | function cloneTick() { 921 | return g.select(function() { return this.appendChild(g.select("text").node().cloneNode(true)); }) 922 | .style("display", "none") 923 | .text(null); 924 | } 925 | } 926 | 927 | axis.remove = function(selection) { 928 | 929 | selection.selectAll("svg") 930 | .each(remove) 931 | .remove(); 932 | 933 | function remove(d) { 934 | context.on("change.axis-" + d.id, null); 935 | context.on("focus.axis-" + d.id, null); 936 | } 937 | }; 938 | 939 | return d3.rebind(axis, axis_, 940 | "orient", 941 | "ticks", 942 | "tickSubdivide", 943 | "tickSize", 944 | "tickPadding", 945 | "tickFormat"); 946 | }; 947 | 948 | var cubism_axisFormatSeconds = d3.time.format("%I:%M:%S %p"), 949 | cubism_axisFormatMinutes = d3.time.format("%I:%M %p"); 950 | cubism_contextPrototype.rule = function() { 951 | var context = this; 952 | 953 | function rule(selection) { 954 | var id = ++cubism_id; 955 | 956 | var line = selection.append("div") 957 | .datum({id: id}) 958 | .attr("class", "line") 959 | .style("position", "fixed") 960 | .style("top", 0) 961 | .style("right", 0) 962 | .style("bottom", 0) 963 | .style("width", "1px") 964 | .style("pointer-events", "none"); 965 | 966 | context.on("focus.rule-" + id, function(i) { 967 | line 968 | .style("display", i == null ? "none" : null) 969 | .style("left", function() { return this.parentNode.getBoundingClientRect().left + i + "px"; }); 970 | }); 971 | } 972 | 973 | rule.remove = function(selection) { 974 | 975 | selection.selectAll(".line") 976 | .each(remove) 977 | .remove(); 978 | 979 | function remove(d) { 980 | context.on("focus.rule-" + d.id, null); 981 | } 982 | }; 983 | 984 | return rule; 985 | }; 986 | })(this); 987 | -------------------------------------------------------------------------------- /_site/js/libs/jquery-1.7.2.min.js: -------------------------------------------------------------------------------- 1 | /*! jQuery v1.7.2 jquery.com | jquery.org/license */ 2 | (function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cu(a){if(!cj[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ck||(ck=c.createElement("iframe"),ck.frameBorder=ck.width=ck.height=0),b.appendChild(ck);if(!cl||!ck.createElement)cl=(ck.contentWindow||ck.contentDocument).document,cl.write((f.support.boxModel?"":"")+""),cl.close();d=cl.createElement(a),cl.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ck)}cj[a]=e}return cj[a]}function ct(a,b){var c={};f.each(cp.concat.apply([],cp.slice(0,b)),function(){c[this]=a});return c}function cs(){cq=b}function cr(){setTimeout(cs,0);return cq=f.now()}function ci(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ch(){try{return new a.XMLHttpRequest}catch(b){}}function cb(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g0){if(c!=="border")for(;e=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?+d:j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.2",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){if(typeof c!="string"||!c)return null;var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c
a",d=p.getElementsByTagName("*"),e=p.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=p.getElementsByTagName("input")[0],b={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:p.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,pixelMargin:!0},f.boxModel=b.boxModel=c.compatMode==="CSS1Compat",i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete p.test}catch(r){b.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",function(){b.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),i.setAttribute("name","t"),p.appendChild(i),j=c.createDocumentFragment(),j.appendChild(p.lastChild),b.checkClone=j.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,j.removeChild(i),j.appendChild(p);if(p.attachEvent)for(n in{submit:1,change:1,focusin:1})m="on"+n,o=m in p,o||(p.setAttribute(m,"return;"),o=typeof p[m]=="function"),b[n+"Bubbles"]=o;j.removeChild(p),j=g=h=p=i=null,f(function(){var d,e,g,h,i,j,l,m,n,q,r,s,t,u=c.getElementsByTagName("body")[0];!u||(m=1,t="padding:0;margin:0;border:",r="position:absolute;top:0;left:0;width:1px;height:1px;",s=t+"0;visibility:hidden;",n="style='"+r+t+"5px solid #000;",q="
"+""+"
",d=c.createElement("div"),d.style.cssText=s+"width:0;height:0;position:static;top:0;margin-top:"+m+"px",u.insertBefore(d,u.firstChild),p=c.createElement("div"),d.appendChild(p),p.innerHTML="
t
",k=p.getElementsByTagName("td"),o=k[0].offsetHeight===0,k[0].style.display="",k[1].style.display="none",b.reliableHiddenOffsets=o&&k[0].offsetHeight===0,a.getComputedStyle&&(p.innerHTML="",l=c.createElement("div"),l.style.width="0",l.style.marginRight="0",p.style.width="2px",p.appendChild(l),b.reliableMarginRight=(parseInt((a.getComputedStyle(l,null)||{marginRight:0}).marginRight,10)||0)===0),typeof p.style.zoom!="undefined"&&(p.innerHTML="",p.style.width=p.style.padding="1px",p.style.border=0,p.style.overflow="hidden",p.style.display="inline",p.style.zoom=1,b.inlineBlockNeedsLayout=p.offsetWidth===3,p.style.display="block",p.style.overflow="visible",p.innerHTML="
",b.shrinkWrapBlocks=p.offsetWidth!==3),p.style.cssText=r+s,p.innerHTML=q,e=p.firstChild,g=e.firstChild,i=e.nextSibling.firstChild.firstChild,j={doesNotAddBorder:g.offsetTop!==5,doesAddBorderForTableAndCells:i.offsetTop===5},g.style.position="fixed",g.style.top="20px",j.fixedPosition=g.offsetTop===20||g.offsetTop===15,g.style.position=g.style.top="",e.style.overflow="hidden",e.style.position="relative",j.subtractsBorderForOverflowNotVisible=g.offsetTop===-5,j.doesNotIncludeMarginInBodyOffset=u.offsetTop!==m,a.getComputedStyle&&(p.style.marginTop="1%",b.pixelMargin=(a.getComputedStyle(p,null)||{marginTop:0}).marginTop!=="1%"),typeof d.style.zoom!="undefined"&&(d.style.zoom=1),u.removeChild(d),l=p=d=null,f.extend(b,j))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e1,null,!1)},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,b){a&&(b=(b||"fx")+"mark",f._data(a,b,(f._data(a,b)||0)+1))},_unmark:function(a,b,c){a!==!0&&(c=b,b=a,a=!1);if(b){c=c||"fx";var d=c+"mark",e=a?0:(f._data(b,d)||1)-1;e?f._data(b,d,e):(f.removeData(b,d,!0),n(b,c,"mark"))}},queue:function(a,b,c){var d;if(a){b=(b||"fx")+"queue",d=f._data(a,b),c&&(!d||f.isArray(c)?d=f._data(a,b,f.makeArray(c)):d.push(c));return d||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e={};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),f._data(a,b+".run",e),d.call(a,function(){f.dequeue(a,b)},e)),c.length||(f.removeData(a,b+"queue "+b+".run",!0),n(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){var d=2;typeof a!="string"&&(c=a,a="fx",d--);if(arguments.length1)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,f.prop,a,b,arguments.length>1)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(p);for(c=0,d=this.length;c-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.type]||f.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.type]||f.valHooks[g.nodeName.toLowerCase()];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h,i=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;i=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/(?:^|\s)hover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function( 3 | a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")};f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler,g=p.selector),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;le&&j.push({elem:this,matches:d.slice(e)});for(k=0;k0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));o.match.globalPOS=p;var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h0)for(h=g;h=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/]","i"),bd=/checked\s*(?:[^=]|=\s*.checked.)/i,be=/\/(java|ecma)script/i,bf=/^\s*",""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div
","
"]),f.fn.extend({text:function(a){return f.access(this,function(a){return a===b?f.text(this):this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f 4 | .clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){return f.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1>");try{for(;d1&&l0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||f.isXMLDoc(a)||!bc.test("<"+a.nodeName+">")?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g,h,i,j=[];b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);for(var k=0,l;(l=a[k])!=null;k++){typeof l=="number"&&(l+="");if(!l)continue;if(typeof l=="string")if(!_.test(l))l=b.createTextNode(l);else{l=l.replace(Y,"<$1>");var m=(Z.exec(l)||["",""])[1].toLowerCase(),n=bg[m]||bg._default,o=n[0],p=b.createElement("div"),q=bh.childNodes,r;b===c?bh.appendChild(p):U(b).appendChild(p),p.innerHTML=n[1]+l+n[2];while(o--)p=p.lastChild;if(!f.support.tbody){var s=$.test(l),t=m==="table"&&!s?p.firstChild&&p.firstChild.childNodes:n[1]===""&&!s?p.childNodes:[];for(i=t.length-1;i>=0;--i)f.nodeName(t[i],"tbody")&&!t[i].childNodes.length&&t[i].parentNode.removeChild(t[i])}!f.support.leadingWhitespace&&X.test(l)&&p.insertBefore(b.createTextNode(X.exec(l)[0]),p.firstChild),l=p.childNodes,p&&(p.parentNode.removeChild(p),q.length>0&&(r=q[q.length-1],r&&r.parentNode&&r.parentNode.removeChild(r)))}var u;if(!f.support.appendChecked)if(l[0]&&typeof (u=l.length)=="number")for(i=0;i1)},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=by(a,"opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=bu.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(by)return by(a,c)},swap:function(a,b,c){var d={},e,f;for(f in b)d[f]=a.style[f],a.style[f]=b[f];e=c.call(a);for(f in b)a.style[f]=d[f];return e}}),f.curCSS=f.css,c.defaultView&&c.defaultView.getComputedStyle&&(bz=function(a,b){var c,d,e,g,h=a.style;b=b.replace(br,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b))),!f.support.pixelMargin&&e&&bv.test(b)&&bt.test(c)&&(g=h.width,h.width=c,c=e.width,h.width=g);return c}),c.documentElement.currentStyle&&(bA=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f==null&&g&&(e=g[b])&&(f=e),bt.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),by=bz||bA,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth!==0?bB(a,b,d):f.swap(a,bw,function(){return bB(a,b,d)})},set:function(a,b){return bs.test(b)?b+"px":b}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bq.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bp,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bp.test(g)?g.replace(bp,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){return f.swap(a,{display:"inline-block"},function(){return b?by(a,"margin-right"):a.style.marginRight})}})}),f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)}),f.each({margin:"",padding:"",border:"Width"},function(a,b){f.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bx[d]+b]=e[d]||e[d-2]||e[0];return f}}});var bC=/%20/g,bD=/\[\]$/,bE=/\r?\n/g,bF=/#.*$/,bG=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bH=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bI=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bJ=/^(?:GET|HEAD)$/,bK=/^\/\//,bL=/\?/,bM=/)<[^<]*)*<\/script>/gi,bN=/^(?:select|textarea)/i,bO=/\s+/,bP=/([?&])_=[^&]*/,bQ=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bR=f.fn.load,bS={},bT={},bU,bV,bW=["*/"]+["*"];try{bU=e.href}catch(bX){bU=c.createElement("a"),bU.href="",bU=bU.href}bV=bQ.exec(bU.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bR)return bR.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("
").append(c.replace(bM,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bN.test(this.nodeName)||bH.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bE,"\r\n")}}):{name:b.name,value:c.replace(bE,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b$(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b$(a,b);return a},ajaxSettings:{url:bU,isLocal:bI.test(bV[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bW},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bY(bS),ajaxTransport:bY(bT),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?ca(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cb(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bG.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bF,"").replace(bK,bV[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bO),d.crossDomain==null&&(r=bQ.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bV[1]&&r[2]==bV[2]&&(r[3]||(r[1]==="http:"?80:443))==(bV[3]||(bV[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),bZ(bS,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bJ.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bL.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bP,"$1_="+x);d.url=y+(y===d.url?(bL.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bW+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=bZ(bT,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)b_(g,a[g],c,e);return d.join("&").replace(bC,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cc=f.now(),cd=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cc++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=typeof b.data=="string"&&/^application\/x\-www\-form\-urlencoded/.test(b.contentType);if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(cd.test(b.url)||e&&cd.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(cd,l),b.url===j&&(e&&(k=k.replace(cd,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var ce=a.ActiveXObject?function(){for(var a in cg)cg[a](0,1)}:!1,cf=0,cg;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ch()||ci()}:ch,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,ce&&delete cg[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n);try{m.text=h.responseText}catch(a){}try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cf,ce&&(cg||(cg={},f(a).unload(ce)),cg[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cj={},ck,cl,cm=/^(?:toggle|show|hide)$/,cn=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,co,cp=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cq;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(ct("show",3),a,b,c);for(var g=0,h=this.length;g=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);f.fn[a]=function(e){return f.access(this,function(a,e,g){var h=cy(a);if(g===b)return h?c in h?h[c]:f.support.boxModel&&h.document.documentElement[e]||h.document.body[e]:a[e];h?h.scrollTo(d?f(h).scrollLeft():g,d?g:f(h).scrollTop()):a[e]=g},a,e,arguments.length,null)}}),f.each({Height:"height",Width:"width"},function(a,c){var d="client"+a,e="scroll"+a,g="offset"+a;f.fn["inner"+a]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,c,"padding")):this[c]():null},f.fn["outer"+a]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,c,a?"margin":"border")):this[c]():null},f.fn[c]=function(a){return f.access(this,function(a,c,h){var i,j,k,l;if(f.isWindow(a)){i=a.document,j=i.documentElement[d];return f.support.boxModel&&j||i.body&&i.body[d]||j}if(a.nodeType===9){i=a.documentElement;if(i[d]>=i[e])return i[d];return Math.max(a.body[e],i[e],a.body[g],i[g])}if(h===b){k=f.css(a,c),l=parseFloat(k);return f.isNumeric(l)?l:k}f(a).css(c,h)},c,a,arguments.length,null)}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window); 5 | -------------------------------------------------------------------------------- /elasticsearch-paramedic-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karmi/elasticsearch-paramedic/0c8df1654d6574a6509c9599a63413dab046305b/elasticsearch-paramedic-screenshot.png -------------------------------------------------------------------------------- /plugin-descriptor.properties: -------------------------------------------------------------------------------- 1 | description=Paramedic is a simple tool to monitor and inspect ElasticSearch clusters 2 | version=2.0 3 | name=paramedic 4 | site=true 5 | --------------------------------------------------------------------------------