├── .gitignore ├── CHANGELOG.md ├── CNAME ├── README.md ├── css ├── bootstrap-responsive.css ├── bootstrap-responsive.min.css ├── bootstrap.css ├── bootstrap.min.css ├── detail.css ├── font-awesome-ie7.css ├── font-awesome.css ├── graph.css ├── legend.css └── main.css ├── dashboards.js ├── favicon.ico ├── font ├── fontawesome-webfont.eot ├── fontawesome-webfont.svg ├── fontawesome-webfont.ttf └── fontawesome-webfont.woff ├── img ├── giraffe-bg.png ├── giraffe.png ├── glyphicons-halflings-white.png └── glyphicons-halflings.png ├── index.html └── js ├── giraffe-annotator.js ├── giraffe.js ├── src └── giraffe.coffee └── vendor ├── Markdown.Converter.js ├── bootstrap.js ├── bootstrap.min.js ├── d3.v3.min.js ├── jquery-1.8.3.min.js ├── jquery-ui.min.js ├── jquery.ba-bbq.min.js ├── jquery.rule-1.0.2-min.js ├── modernizr-2.6.2-respond-1.1.0.min.js ├── mustache.js ├── rickshaw.min.js └── underscore-min.js /.gitignore: -------------------------------------------------------------------------------- 1 | *.sw[po] 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog for Giraffe 2 | 3 | * Version 1.3.1 - fixed width bug (#69) 4 | * Version 1.3.0 - updated Rickshaw to latest version 5 | * Version 1.2.0 - added support for offset param 6 | * Version 1.1.1 - Moving the X axis text down 7 | * Version 1.1.0 - added support for renderer: multi 8 | * Version 1.0.3 - bug fix for `min: 0` (#55) 9 | * Version 1.0.2 - more configuration options 10 | - Stroke color in area renderer can be a function that takes the graph color 11 | as a d3.rgb color and returns the color of the stroke 12 | - Format y axis ticks and totals values with a formatting function, 13 | `ticks_formatter` and `totals_formatter` respectively, 14 | just like `summary_formatter` 15 | - Totals fields can be selected by passing an array of strings, e.g. 16 | `"totals_fields": ["max", "min"]` 17 | * Version 1.0.1 - min default set to `auto` (#49) 18 | * Version 1.0.0 19 | - first tagged version 20 | 21 | ## commits 22 | 23 | [github commit log](https://github.com/kenhub/giraffe/commits) 24 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | giraffe.kenhub.com 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Giraffe : A [Graphite](http://graphite.wikidot.com) Dashboard with a long neck ![giraffe logo](https://raw.github.com/kenhub/giraffe/master/img/giraffe.png) 2 | 3 | ## NOTE 4 | This project isn't actively developed or maintained. There are lots of alternative [visualization tools](http://graphite.readthedocs.io/en/latest/tools.html#visualization) for Graphite and other time-series backends. I don't have any experience using it, but if you're looking for a Graphite dashboard, [Grafana](https://grafana.com/) seems like a good choice. 5 | 6 | ## Don't know Graphite? 7 | 8 | ... then Giraffe is probably not for you. But before you walk away - you should definitely check out graphite! [see 9 | why](http://codeascraft.etsy.com/2011/02/15/measure-anything-measure-everything/). 10 | 11 | Need a quick way to install and play with graphite? try [graphite-fabric](https://github.com/gingerlime/graphite-fabric). 12 | 13 | ## Stack 14 | 15 | Giraffe is based on a number of amazing open-source projects and libraries, to name a few: 16 | 17 | * The [Rickshaw](http://code.shutterstock.com/rickshaw/) charting library (based on [d3](http://mbostock.github.com/d3/)) 18 | * [HTML5 Boilerplate](http://html5boilerplate.com/) and [Bootstrap](https://github.com/twbs/bootstrap) 19 | * Written in (but does not require) [Coffeescript](http://coffeescript.org) 20 | * Other libraries such as [jQuery](http://jquery.com), [underscore.js](http://underscorejs.org), [jQuery BBQ](http://benalman.com/projects/jquery-bbq-plugin/), [pagedown](), [{{mustache}}](https://github.com/janl/mustache.js/) and more 21 | 22 | ## Inspiration 23 | 24 | Giraffe is heavily inspired by several existing graphite dashboards. Primarily: 25 | 26 | * [GDash](https://github.com/ripienaar/gdash) - it uses Bootstrap and allows multiple dashboards to be configured. However, it requires running a sinatra server, and the graphs are pulled directly from graphite rather than rendered via a js charting library. 27 | * [Tasseo](https://github.com/obfuscurity/tasseo) - also allows multiple dashboards, but still relies on a server component. Tasseo also uses Rickshaw, but charts only a single data series. Giraffe started as a tasseo fork, but eventually got refactored (almost) beyond recognition. 28 | * [Graphene](https://github.com/jondot/graphene) - a d3-based relatime dashboard with different widgets. Supports a single dashboard, and its charting functionality is not as extensive as with Rickshaw. 29 | 30 | ## Why another dashboard? 31 | 32 | Because we wanted to create a dashboard that has all the benefits and none of the downsides of the other dashboards. And because it was interesting to try something new. Giraffe is not necessarily better than any of those solutions. It's a different animal. It has an interesting pattern and a funny face. 33 | 34 | ## Benefits 35 | 36 | * **No server required** - Giraffe can be installed on any server, or even run from a folder. Just copy the files and you're done. 37 | * **Beautiful, real-time visualization** - using Rickshaw to create visually appealing, interactive charts. 38 | * **Flexible** - supports many dashboards, different metrics, annotations / events, colour schemes, time intervals, summary options, CSS and more. 39 | * **Easy to use** - configuration is done from [one (javascript) file](https://github.com/kenhub/giraffe/blob/master/dashboards.js) with a reasonbly clear and documented options. You 40 | don't even need to know javascript to configure it. Be aware that it's not very tolerant to typos or missing commas. 41 | 42 | ## Issues 43 | 44 | * There's no such thing as a free lunch 45 | * Consequently, when adding many metrics to a single dashboard, and particularly when metrics have many data points and 46 | series, the experience might get sluggish. With great power comes great responsibility. Design your dashboards with care. 47 | 48 | ## Configuration 49 | 50 | ### Quick overview 51 | 52 | Almost all configuration is placed in one file : [dashboards.js](https://github.com/kenhub/giraffe/blob/master/dashboards.js). Here's a small snippet with some key configuration options: 53 | 54 | ```javascript 55 | var graphite_url = "demo"; // enter your graphite url, e.g. http://your.graphite.com 56 | 57 | var dashboards = 58 | [ 59 | { "name": "Users", // give your dashboard a name (required!) 60 | "refresh": 5000, // each dashboard has its own refresh interval (in ms) 61 | // add an (optional) dashboard description. description can be written in markdown / html. 62 | "description": "User engagement" 63 | +"\n" 64 | +"\nThis dashboard tracks user engagement (signups, registrations etc)" 65 | , 66 | "metrics": // metrics is an array of charts on the dashboard 67 | [ 68 | { 69 | "alias": "signups", // display name for this metric 70 | "target": "sumSeries(enter.your.graphite.metrics.here)", // enter your graphite barebone target expression here 71 | "description": "New signups to the website", // enter your metric description here 72 | "summary": "sum", // available options: [sum|min|max|avg|last|] 73 | "summary_formatter": d3.format(",f"), // customize your summary format function. see d3 formatting docs for more options 74 | }, 75 | { 76 | "alias": "signup breakdown", 77 | "target": "sumSeries(stats.*.event)", // target can use any graphite-supported wildcards 78 | "description": "signup breakdown based on site location", 79 | "renderer": "area", // use any rickshaw-supported renderer 80 | "unstack": true // other parameters like unstack, interpolation, stroke, offset are also available (see rickshaw documentation for more info) 81 | "colspan": 3 // the graph can span across 1,2 or 3 columns 82 | }, 83 | { 84 | "alias": "Registration breakdown", 85 | // target can use a javascript function. This allows using dynamic parameters (e.g. period). See a few functions 86 | // at the bottom of the dashboards.js file. 87 | "target": function() { return 'summarize(events.registration.success,"' + entire_period() + 'min)' }, 88 | "renderer": "bar", 89 | "description": "Registrations based on channel", 90 | "hover_formatter": d3.format("03.6g"), // customize your hover format 91 | }, 92 | { 93 | "alias": "Logins", 94 | "targets": ['alias(events.login.success,"success login")', // targets array is also supported 95 | 'alias(events.login.fail,"login failure")'], // as well as specifying colors 96 | // see below and in dashboards.js for more advanced options 97 | "renderer": "bar", 98 | "description": "Logins to the website", 99 | "null_as": 0 // null values are normally ignored, but you can convert null to a specific value (usually zero) 100 | }, 101 | ] 102 | }, 103 | ... 104 | 105 | ``` 106 | 107 | #### target(s) 108 | 109 | One of the key parameters for each metric is its `target`, corresponding to the [graphite 110 | target](http://graphite.readthedocs.org/en/latest/render_api.html#target). 111 | 112 | a metric target(s) can have one of the following: 113 | 114 | * a `string` - describing a graphite target 115 | * a `function` - returning a string with a graphite target 116 | * an array of targets in one of the following formats: 117 | * `string` 118 | * `function` 119 | * dictionary in the form 120 | ```javascript 121 | 122 | { 123 | target: 'target', // usually a target will include the [alias](http://graphite.readthedocs.org/en/0.9.10/functions.html#graphite.render.functions.alias) function 124 | alias: 'graphite_alias', // only if an alias is specified in the target, add an alias field corresponding to the graphite alias 125 | color: '#f00' // an RGB color value can be specified for this target 126 | } 127 | ``` 128 | 129 | #### annotations and events 130 | 131 | Giraffe supports [annotations](http://code.shutterstock.com/rickshaw/#annotations) from two potential data sources: 132 | 133 | * standard graphite targets - use `annotator` to specify your target to collect events from. Each value 134 | different from None or zero will be annotated. You can optionally specify a description for all annotations. 135 | The default value is `deployment` 136 | * graphite [events](https://code.launchpad.net/~lucio.torre/graphite/add-events/+merge/69142) - use `events` to specify the tags you want to include as annotations, or `*` for all tags. tags are space separated. Using `events` has the benefit of including the event `what` and `data` for each annotation (as opposed to one value for all annotations) 137 | 138 | ##### Notes: 139 | 140 | * even though you can use an annotator with a target of `events('*')` it will be very slow on bigger time frames. 141 | `"events": "*"` will be far more efficient. 142 | * do not mix `annotator` and `events` within the same metric. 143 | * The graphite events handler [does not currently support jsonp](https://github.com/graphite-project/graphite-web/pull/128). 144 | Until it does, you can do one of the following: 145 | * +1 it on the graphite issue tracker so it gets included :) 146 | * install giraffe on the same server as your graphite to avoid cross-domain issues. 147 | * configure [CORS](http://www.w3.org/wiki/CORS_Enabled#At_the_HTTP_Server_level...) on your graphite web server. 148 | * install the kenhub graphite branch `pip install git+git://github.com/kenhub/graphite-web.git@0.9.x`which adds jsonp 149 | support. At least until graphite supports it. 150 | 151 | thanks to @mattpascoe for his suggestion and help testing this. 152 | 153 | ### More configuration options 154 | 155 | * see [dashboards.js](https://github.com/kenhub/giraffe/blob/master/dashboards.js) 156 | * check out the [demo](http://kenhub.github.com/giraffe/) to see some of the configuration options in-action 157 | * Clone the repository or [download](https://github.com/kenhub/giraffe/archive/master.zip) and take your giraffe for a spin. You can run it from your desktop. 158 | 159 | ## Development 160 | 161 | No longer actively maintained. 162 | 163 | The core code lives in `js/src/giraffe.coffee`. 164 | Since the `dashboards.js` configuration needs easy access to everything inside `giraffe.js`, please compile the coffeescript 165 | using the `--bare` option. 166 | 167 | 168 | ## Who is behind Giraffe? 169 | 170 | Giraffe was developed at [kenHub](https://www.kenhub.com). We are not much of techie startup, but we hope to build the 171 | best tools for learning anatomy and medicine online. To do that, we wanted to be able to measure our application, 172 | user-engagement and many other aspects. We could have used a 3rd party service, but it was more fun to build our own. 173 | It's also a chance to contribute to the open source community, which we love so much. 174 | 175 | If you use Giraffe and like it, we would really appreciate if you could star this project, link to this github page, and 176 | tell others. We hope to build a growing community around this project. 177 | 178 | We are a very tiny startup with no money, great ideas and good intentions. It would **help us a lot** if you 179 | **link to www.kenhub.com** and tell people who are interested in learning anatomy about us. 180 | 181 | ## License 182 | Giraffe is distributed under the MIT license. All 3rd party libraries and components are distributed under their 183 | respective license terms. 184 | 185 | The Giraffe icon and image were produced using Rickshaw :) 186 | 187 | ``` 188 | Copyright (C) 2012 kenHub GmbH 189 | 190 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 191 | documentation files (the "Software"), to deal in the Software without restriction, including without limitation the 192 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit 193 | persons to whom the Software is furnished to do so, subject to the following conditions: 194 | 195 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the 196 | Software. 197 | 198 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 199 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 200 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 201 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 202 | ``` 203 | 204 | ## More? 205 | 206 | Check out the different [demo dashboards](http://kenhub.github.com/giraffe/) for more information about making your own giraffe awesome. 207 | 208 | ## Links, Plugins and 3rd party tools 209 | 210 | * [giraffe-collectd](https://github.com/bflad/giraffe-collectd) - A simple Giraffe configuration generator for collectd metrics in Graphite (created by @bflad) 211 | * [giraffe-web](https://github.com/jedi4ever/giraffe-web.js) - a node.js server wrapper and cli - also allows proxying your graphite server (created by @jedi4ever) 212 | -------------------------------------------------------------------------------- /css/bootstrap-responsive.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Responsive v2.2.1 3 | * 4 | * Copyright 2012 Twitter, Inc 5 | * Licensed under the Apache License v2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Designed and built with all the love in the world @twitter by @mdo and @fat. 9 | */.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.hidden{display:none;visibility:hidden}.visible-phone{display:none!important}.visible-tablet{display:none!important}.hidden-desktop{display:none!important}.visible-desktop{display:inherit!important}@media(min-width:768px) and (max-width:979px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-tablet{display:inherit!important}.hidden-tablet{display:none!important}}@media(max-width:767px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-phone{display:inherit!important}.hidden-phone{display:none!important}}@media(min-width:1200px){.row{margin-left:-30px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:30px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px}.span12{width:1170px}.span11{width:1070px}.span10{width:970px}.span9{width:870px}.span8{width:770px}.span7{width:670px}.span6{width:570px}.span5{width:470px}.span4{width:370px}.span3{width:270px}.span2{width:170px}.span1{width:70px}.offset12{margin-left:1230px}.offset11{margin-left:1130px}.offset10{margin-left:1030px}.offset9{margin-left:930px}.offset8{margin-left:830px}.offset7{margin-left:730px}.offset6{margin-left:630px}.offset5{margin-left:530px}.offset4{margin-left:430px}.offset3{margin-left:330px}.offset2{margin-left:230px}.offset1{margin-left:130px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.564102564102564%;*margin-left:2.5109110747408616%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.564102564102564%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.45299145299145%;*width:91.39979996362975%}.row-fluid .span10{width:82.90598290598291%;*width:82.8527914166212%}.row-fluid .span9{width:74.35897435897436%;*width:74.30578286961266%}.row-fluid .span8{width:65.81196581196582%;*width:65.75877432260411%}.row-fluid .span7{width:57.26495726495726%;*width:57.21176577559556%}.row-fluid .span6{width:48.717948717948715%;*width:48.664757228587014%}.row-fluid .span5{width:40.17094017094017%;*width:40.11774868157847%}.row-fluid .span4{width:31.623931623931625%;*width:31.570740134569924%}.row-fluid .span3{width:23.076923076923077%;*width:23.023731587561375%}.row-fluid .span2{width:14.52991452991453%;*width:14.476723040552828%}.row-fluid .span1{width:5.982905982905983%;*width:5.929714493544281%}.row-fluid .offset12{margin-left:105.12820512820512%;*margin-left:105.02182214948171%}.row-fluid .offset12:first-child{margin-left:102.56410256410257%;*margin-left:102.45771958537915%}.row-fluid .offset11{margin-left:96.58119658119658%;*margin-left:96.47481360247316%}.row-fluid .offset11:first-child{margin-left:94.01709401709402%;*margin-left:93.91071103837061%}.row-fluid .offset10{margin-left:88.03418803418803%;*margin-left:87.92780505546462%}.row-fluid .offset10:first-child{margin-left:85.47008547008548%;*margin-left:85.36370249136206%}.row-fluid .offset9{margin-left:79.48717948717949%;*margin-left:79.38079650845607%}.row-fluid .offset9:first-child{margin-left:76.92307692307693%;*margin-left:76.81669394435352%}.row-fluid .offset8{margin-left:70.94017094017094%;*margin-left:70.83378796144753%}.row-fluid .offset8:first-child{margin-left:68.37606837606839%;*margin-left:68.26968539734497%}.row-fluid .offset7{margin-left:62.393162393162385%;*margin-left:62.28677941443899%}.row-fluid .offset7:first-child{margin-left:59.82905982905982%;*margin-left:59.72267685033642%}.row-fluid .offset6{margin-left:53.84615384615384%;*margin-left:53.739770867430444%}.row-fluid .offset6:first-child{margin-left:51.28205128205128%;*margin-left:51.175668303327875%}.row-fluid .offset5{margin-left:45.299145299145295%;*margin-left:45.1927623204219%}.row-fluid .offset5:first-child{margin-left:42.73504273504273%;*margin-left:42.62865975631933%}.row-fluid .offset4{margin-left:36.75213675213675%;*margin-left:36.645753773413354%}.row-fluid .offset4:first-child{margin-left:34.18803418803419%;*margin-left:34.081651209310785%}.row-fluid .offset3{margin-left:28.205128205128204%;*margin-left:28.0987452264048%}.row-fluid .offset3:first-child{margin-left:25.641025641025642%;*margin-left:25.53464266230224%}.row-fluid .offset2{margin-left:19.65811965811966%;*margin-left:19.551736679396257%}.row-fluid .offset2:first-child{margin-left:17.094017094017094%;*margin-left:16.98763411529369%}.row-fluid .offset1{margin-left:11.11111111111111%;*margin-left:11.004728132387708%}.row-fluid .offset1:first-child{margin-left:8.547008547008547%;*margin-left:8.440625568285142%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:30px}input.span12,textarea.span12,.uneditable-input.span12{width:1156px}input.span11,textarea.span11,.uneditable-input.span11{width:1056px}input.span10,textarea.span10,.uneditable-input.span10{width:956px}input.span9,textarea.span9,.uneditable-input.span9{width:856px}input.span8,textarea.span8,.uneditable-input.span8{width:756px}input.span7,textarea.span7,.uneditable-input.span7{width:656px}input.span6,textarea.span6,.uneditable-input.span6{width:556px}input.span5,textarea.span5,.uneditable-input.span5{width:456px}input.span4,textarea.span4,.uneditable-input.span4{width:356px}input.span3,textarea.span3,.uneditable-input.span3{width:256px}input.span2,textarea.span2,.uneditable-input.span2{width:156px}input.span1,textarea.span1,.uneditable-input.span1{width:56px}.thumbnails{margin-left:-30px}.thumbnails>li{margin-left:30px}.row-fluid .thumbnails{margin-left:0}}@media(min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px}.span12{width:724px}.span11{width:662px}.span10{width:600px}.span9{width:538px}.span8{width:476px}.span7{width:414px}.span6{width:352px}.span5{width:290px}.span4{width:228px}.span3{width:166px}.span2{width:104px}.span1{width:42px}.offset12{margin-left:764px}.offset11{margin-left:702px}.offset10{margin-left:640px}.offset9{margin-left:578px}.offset8{margin-left:516px}.offset7{margin-left:454px}.offset6{margin-left:392px}.offset5{margin-left:330px}.offset4{margin-left:268px}.offset3{margin-left:206px}.offset2{margin-left:144px}.offset1{margin-left:82px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.7624309392265194%;*margin-left:2.709239449864817%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.7624309392265194%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.43646408839778%;*width:91.38327259903608%}.row-fluid .span10{width:82.87292817679558%;*width:82.81973668743387%}.row-fluid .span9{width:74.30939226519337%;*width:74.25620077583166%}.row-fluid .span8{width:65.74585635359117%;*width:65.69266486422946%}.row-fluid .span7{width:57.18232044198895%;*width:57.12912895262725%}.row-fluid .span6{width:48.61878453038674%;*width:48.56559304102504%}.row-fluid .span5{width:40.05524861878453%;*width:40.00205712942283%}.row-fluid .span4{width:31.491712707182323%;*width:31.43852121782062%}.row-fluid .span3{width:22.92817679558011%;*width:22.87498530621841%}.row-fluid .span2{width:14.3646408839779%;*width:14.311449394616199%}.row-fluid .span1{width:5.801104972375691%;*width:5.747913483013988%}.row-fluid .offset12{margin-left:105.52486187845304%;*margin-left:105.41847889972962%}.row-fluid .offset12:first-child{margin-left:102.76243093922652%;*margin-left:102.6560479605031%}.row-fluid .offset11{margin-left:96.96132596685082%;*margin-left:96.8549429881274%}.row-fluid .offset11:first-child{margin-left:94.1988950276243%;*margin-left:94.09251204890089%}.row-fluid .offset10{margin-left:88.39779005524862%;*margin-left:88.2914070765252%}.row-fluid .offset10:first-child{margin-left:85.6353591160221%;*margin-left:85.52897613729868%}.row-fluid .offset9{margin-left:79.8342541436464%;*margin-left:79.72787116492299%}.row-fluid .offset9:first-child{margin-left:77.07182320441989%;*margin-left:76.96544022569647%}.row-fluid .offset8{margin-left:71.2707182320442%;*margin-left:71.16433525332079%}.row-fluid .offset8:first-child{margin-left:68.50828729281768%;*margin-left:68.40190431409427%}.row-fluid .offset7{margin-left:62.70718232044199%;*margin-left:62.600799341718584%}.row-fluid .offset7:first-child{margin-left:59.94475138121547%;*margin-left:59.838368402492065%}.row-fluid .offset6{margin-left:54.14364640883978%;*margin-left:54.037263430116376%}.row-fluid .offset6:first-child{margin-left:51.38121546961326%;*margin-left:51.27483249088986%}.row-fluid .offset5{margin-left:45.58011049723757%;*margin-left:45.47372751851417%}.row-fluid .offset5:first-child{margin-left:42.81767955801105%;*margin-left:42.71129657928765%}.row-fluid .offset4{margin-left:37.01657458563536%;*margin-left:36.91019160691196%}.row-fluid .offset4:first-child{margin-left:34.25414364640884%;*margin-left:34.14776066768544%}.row-fluid .offset3{margin-left:28.45303867403315%;*margin-left:28.346655695309746%}.row-fluid .offset3:first-child{margin-left:25.69060773480663%;*margin-left:25.584224756083227%}.row-fluid .offset2{margin-left:19.88950276243094%;*margin-left:19.783119783707537%}.row-fluid .offset2:first-child{margin-left:17.12707182320442%;*margin-left:17.02068884448102%}.row-fluid .offset1{margin-left:11.32596685082873%;*margin-left:11.219583872105325%}.row-fluid .offset1:first-child{margin-left:8.56353591160221%;*margin-left:8.457152932878806%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:710px}input.span11,textarea.span11,.uneditable-input.span11{width:648px}input.span10,textarea.span10,.uneditable-input.span10{width:586px}input.span9,textarea.span9,.uneditable-input.span9{width:524px}input.span8,textarea.span8,.uneditable-input.span8{width:462px}input.span7,textarea.span7,.uneditable-input.span7{width:400px}input.span6,textarea.span6,.uneditable-input.span6{width:338px}input.span5,textarea.span5,.uneditable-input.span5{width:276px}input.span4,textarea.span4,.uneditable-input.span4{width:214px}input.span3,textarea.span3,.uneditable-input.span3{width:152px}input.span2,textarea.span2,.uneditable-input.span2{width:90px}input.span1,textarea.span1,.uneditable-input.span1{width:28px}}@media(max-width:767px){body{padding-right:20px;padding-left:20px}.navbar-fixed-top,.navbar-fixed-bottom,.navbar-static-top{margin-right:-20px;margin-left:-20px}.container-fluid{padding:0}.dl-horizontal dt{float:none;width:auto;clear:none;text-align:left}.dl-horizontal dd{margin-left:0}.container{width:auto}.row-fluid{width:100%}.row,.thumbnails{margin-left:0}.thumbnails>li{float:none;margin-left:0}[class*="span"],.uneditable-input[class*="span"],.row-fluid [class*="span"]{display:block;float:none;width:100%;margin-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.span12,.row-fluid .span12{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="offset"]:first-child{margin-left:0}.input-large,.input-xlarge,.input-xxlarge,input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.input-prepend input,.input-append input,.input-prepend input[class*="span"],.input-append input[class*="span"]{display:inline-block;width:auto}.controls-row [class*="span"]+[class*="span"]{margin-left:0}.modal{position:fixed;top:20px;right:20px;left:20px;width:auto;margin:0}.modal.fade{top:-100px}.modal.fade.in{top:20px}}@media(max-width:480px){.nav-collapse{-webkit-transform:translate3d(0,0,0)}.page-header h1 small{display:block;line-height:20px}input[type="checkbox"],input[type="radio"]{border:1px solid #ccc}.form-horizontal .control-label{float:none;width:auto;padding-top:0;text-align:left}.form-horizontal .controls{margin-left:0}.form-horizontal .control-list{padding-top:0}.form-horizontal .form-actions{padding-right:10px;padding-left:10px}.media .pull-left,.media .pull-right{display:block;float:none;margin-bottom:10px}.media-object{margin-right:0;margin-left:0}.modal{top:10px;right:10px;left:10px}.modal-header .close{padding:10px;margin:-10px}.carousel-caption{position:static}}@media(max-width:979px){body{padding-top:0}.navbar-fixed-top,.navbar-fixed-bottom{position:static}.navbar-fixed-top{margin-bottom:20px}.navbar-fixed-bottom{margin-top:20px}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding:5px}.navbar .container{width:auto;padding:0}.navbar .brand{padding-right:10px;padding-left:10px;margin:0 0 0 -5px}.nav-collapse{clear:both}.nav-collapse .nav{float:none;margin:0 0 10px}.nav-collapse .nav>li{float:none}.nav-collapse .nav>li>a{margin-bottom:2px}.nav-collapse .nav>.divider-vertical{display:none}.nav-collapse .nav .nav-header{color:#777;text-shadow:none}.nav-collapse .nav>li>a,.nav-collapse .dropdown-menu a{padding:9px 15px;font-weight:bold;color:#777;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.nav-collapse .btn{padding:4px 10px 4px;font-weight:normal;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.nav-collapse .dropdown-menu li+li a{margin-bottom:2px}.nav-collapse .nav>li>a:hover,.nav-collapse .dropdown-menu a:hover{background-color:#f2f2f2}.navbar-inverse .nav-collapse .nav>li>a,.navbar-inverse .nav-collapse .dropdown-menu a{color:#999}.navbar-inverse .nav-collapse .nav>li>a:hover,.navbar-inverse .nav-collapse .dropdown-menu a:hover{background-color:#111}.nav-collapse.in .btn-group{padding:0;margin-top:5px}.nav-collapse .dropdown-menu{position:static;top:auto;left:auto;display:none;float:none;max-width:none;padding:0;margin:0 15px;background-color:transparent;border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.nav-collapse .open>.dropdown-menu{display:block}.nav-collapse .dropdown-menu:before,.nav-collapse .dropdown-menu:after{display:none}.nav-collapse .dropdown-menu .divider{display:none}.nav-collapse .nav>li>.dropdown-menu:before,.nav-collapse .nav>li>.dropdown-menu:after{display:none}.nav-collapse .navbar-form,.nav-collapse .navbar-search{float:none;padding:10px 15px;margin:10px 0;border-top:1px solid #f2f2f2;border-bottom:1px solid #f2f2f2;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1)}.navbar-inverse .nav-collapse .navbar-form,.navbar-inverse .nav-collapse .navbar-search{border-top-color:#111;border-bottom-color:#111}.navbar .nav-collapse .nav.pull-right{float:none;margin-left:0}.nav-collapse,.nav-collapse.collapse{height:0;overflow:hidden}.navbar .btn-navbar{display:block}.navbar-static .navbar-inner{padding-right:10px;padding-left:10px}}@media(min-width:980px){.nav-collapse.collapse{height:auto!important;overflow:visible!important}} 10 | -------------------------------------------------------------------------------- /css/detail.css: -------------------------------------------------------------------------------- 1 | .rickshaw_graph .detail { 2 | pointer-events: none; 3 | position: absolute; 4 | top: 0; 5 | z-index: 2; 6 | background: rgba(0, 0, 0, 0.1); 7 | bottom: 0; 8 | width: 1px; 9 | transition: opacity 0.25s linear; 10 | -moz-transition: opacity 0.25s linear; 11 | -o-transition: opacity 0.25s linear; 12 | -webkit-transition: opacity 0.25s linear; 13 | } 14 | .rickshaw_graph .detail.inactive { 15 | opacity: 0; 16 | } 17 | .rickshaw_graph .detail .item.active { 18 | opacity: 1; 19 | } 20 | .rickshaw_graph .detail .x_label { 21 | font-family: Arial, sans-serif; 22 | border-radius: 3px; 23 | padding: 4px; 24 | opacity: 0.5; 25 | border: 1px solid #e0e0e0; 26 | font-size: 12px; 27 | position: absolute; 28 | background: white; 29 | white-space: nowrap; 30 | top: -73px; 31 | } 32 | .rickshaw_graph .detail .item { 33 | position: absolute; 34 | z-index: 2; 35 | border-radius: 3px; 36 | padding: 0.25em; 37 | font-size: 12px; 38 | font-family: Arial, sans-serif; 39 | opacity: 0; 40 | background: rgba(0, 0, 0, 0.4); 41 | color: white; 42 | border: 1px solid rgba(0, 0, 0, 0.4); 43 | margin-left: 1em; 44 | margin-top: -1em; 45 | white-space: nowrap; 46 | } 47 | .rickshaw_graph .detail .item.active { 48 | opacity: 1; 49 | background: rgba(0, 0, 0, 0.8); 50 | } 51 | .rickshaw_graph .detail .item:before { 52 | content: "\25c2"; 53 | position: absolute; 54 | left: -0.5em; 55 | color: rgba(0, 0, 0, 0.7); 56 | width: 0; 57 | } 58 | .rickshaw_graph .detail .dot { 59 | width: 4px; 60 | height: 4px; 61 | margin-left: -4px; 62 | margin-top: -3px; 63 | border-radius: 5px; 64 | position: absolute; 65 | box-shadow: 0 0 2px rgba(0, 0, 0, 0.6); 66 | background: white; 67 | border-width: 2px; 68 | border-style: solid; 69 | display: none; 70 | background-clip: padding-box; 71 | } 72 | .rickshaw_graph .detail .dot.active { 73 | display: block; 74 | } 75 | -------------------------------------------------------------------------------- /css/font-awesome.css: -------------------------------------------------------------------------------- 1 | /* Font Awesome 2 | the iconic font designed for use with Twitter Bootstrap 3 | ------------------------------------------------------- 4 | The full suite of pictographic icons, examples, and documentation 5 | can be found at: http://fortawesome.github.com/Font-Awesome/ 6 | 7 | License 8 | ------------------------------------------------------- 9 | The Font Awesome webfont, CSS, and LESS files are licensed under CC BY 3.0: 10 | http://creativecommons.org/licenses/by/3.0/ A mention of 11 | 'Font Awesome - http://fortawesome.github.com/Font-Awesome' in human-readable 12 | source code is considered acceptable attribution (most common on the web). 13 | If human readable source code is not available to the end user, a mention in 14 | an 'About' or 'Credits' screen is considered acceptable (most common in desktop 15 | or mobile software). 16 | 17 | Contact 18 | ------------------------------------------------------- 19 | Email: dave@davegandy.com 20 | Twitter: http://twitter.com/fortaweso_me 21 | Work: http://lemonwi.se co-founder 22 | 23 | */ 24 | @font-face { 25 | font-family: "FontAwesome"; 26 | src: url('../font/fontawesome-webfont.eot'); 27 | src: url('../font/fontawesome-webfont.eot?#iefix') format('eot'), url('../font/fontawesome-webfont.woff') format('woff'), url('../font/fontawesome-webfont.ttf') format('truetype'), url('../font/fontawesome-webfont.svg#FontAwesome') format('svg'); 28 | font-weight: normal; 29 | font-style: normal; 30 | } 31 | 32 | /* Font Awesome styles 33 | ------------------------------------------------------- */ 34 | [class^="icon-"]:before, [class*=" icon-"]:before { 35 | font-family: FontAwesome; 36 | font-weight: normal; 37 | font-style: normal; 38 | display: inline-block; 39 | text-decoration: inherit; 40 | } 41 | a [class^="icon-"], a [class*=" icon-"] { 42 | display: inline-block; 43 | text-decoration: inherit; 44 | } 45 | /* makes the font 33% larger relative to the icon container */ 46 | .icon-large:before { 47 | vertical-align: top; 48 | font-size: 1.3333333333333333em; 49 | } 50 | .btn [class^="icon-"], .btn [class*=" icon-"] { 51 | /* keeps button heights with and without icons the same */ 52 | 53 | line-height: .9em; 54 | } 55 | li [class^="icon-"], li [class*=" icon-"] { 56 | display: inline-block; 57 | width: 1.25em; 58 | text-align: center; 59 | } 60 | li .icon-large[class^="icon-"], li .icon-large[class*=" icon-"] { 61 | /* 1.5 increased font size for icon-large * 1.25 width */ 62 | 63 | width: 1.875em; 64 | } 65 | li[class^="icon-"], li[class*=" icon-"] { 66 | margin-left: 0; 67 | list-style-type: none; 68 | } 69 | li[class^="icon-"]:before, li[class*=" icon-"]:before { 70 | text-indent: -2em; 71 | text-align: center; 72 | } 73 | li[class^="icon-"].icon-large:before, li[class*=" icon-"].icon-large:before { 74 | text-indent: -1.3333333333333333em; 75 | } 76 | /* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen 77 | readers do not read off random characters that represent icons */ 78 | .icon-glass:before { content: "\f000"; } 79 | .icon-music:before { content: "\f001"; } 80 | .icon-search:before { content: "\f002"; } 81 | .icon-envelope:before { content: "\f003"; } 82 | .icon-heart:before { content: "\f004"; } 83 | .icon-star:before { content: "\f005"; } 84 | .icon-star-empty:before { content: "\f006"; } 85 | .icon-user:before { content: "\f007"; } 86 | .icon-film:before { content: "\f008"; } 87 | .icon-th-large:before { content: "\f009"; } 88 | .icon-th:before { content: "\f00a"; } 89 | .icon-th-list:before { content: "\f00b"; } 90 | .icon-ok:before { content: "\f00c"; } 91 | .icon-remove:before { content: "\f00d"; } 92 | .icon-zoom-in:before { content: "\f00e"; } 93 | 94 | .icon-zoom-out:before { content: "\f010"; } 95 | .icon-off:before { content: "\f011"; } 96 | .icon-signal:before { content: "\f012"; } 97 | .icon-cog:before { content: "\f013"; } 98 | .icon-trash:before { content: "\f014"; } 99 | .icon-home:before { content: "\f015"; } 100 | .icon-file:before { content: "\f016"; } 101 | .icon-time:before { content: "\f017"; } 102 | .icon-road:before { content: "\f018"; } 103 | .icon-download-alt:before { content: "\f019"; } 104 | .icon-download:before { content: "\f01a"; } 105 | .icon-upload:before { content: "\f01b"; } 106 | .icon-inbox:before { content: "\f01c"; } 107 | .icon-play-circle:before { content: "\f01d"; } 108 | .icon-repeat:before { content: "\f01e"; } 109 | 110 | /* \f020 doesn't work in Safari. all shifted one down */ 111 | .icon-refresh:before { content: "\f021"; } 112 | .icon-list-alt:before { content: "\f022"; } 113 | .icon-lock:before { content: "\f023"; } 114 | .icon-flag:before { content: "\f024"; } 115 | .icon-headphones:before { content: "\f025"; } 116 | .icon-volume-off:before { content: "\f026"; } 117 | .icon-volume-down:before { content: "\f027"; } 118 | .icon-volume-up:before { content: "\f028"; } 119 | .icon-qrcode:before { content: "\f029"; } 120 | .icon-barcode:before { content: "\f02a"; } 121 | .icon-tag:before { content: "\f02b"; } 122 | .icon-tags:before { content: "\f02c"; } 123 | .icon-book:before { content: "\f02d"; } 124 | .icon-bookmark:before { content: "\f02e"; } 125 | .icon-print:before { content: "\f02f"; } 126 | 127 | .icon-camera:before { content: "\f030"; } 128 | .icon-font:before { content: "\f031"; } 129 | .icon-bold:before { content: "\f032"; } 130 | .icon-italic:before { content: "\f033"; } 131 | .icon-text-height:before { content: "\f034"; } 132 | .icon-text-width:before { content: "\f035"; } 133 | .icon-align-left:before { content: "\f036"; } 134 | .icon-align-center:before { content: "\f037"; } 135 | .icon-align-right:before { content: "\f038"; } 136 | .icon-align-justify:before { content: "\f039"; } 137 | .icon-list:before { content: "\f03a"; } 138 | .icon-indent-left:before { content: "\f03b"; } 139 | .icon-indent-right:before { content: "\f03c"; } 140 | .icon-facetime-video:before { content: "\f03d"; } 141 | .icon-picture:before { content: "\f03e"; } 142 | 143 | .icon-pencil:before { content: "\f040"; } 144 | .icon-map-marker:before { content: "\f041"; } 145 | .icon-adjust:before { content: "\f042"; } 146 | .icon-tint:before { content: "\f043"; } 147 | .icon-edit:before { content: "\f044"; } 148 | .icon-share:before { content: "\f045"; } 149 | .icon-check:before { content: "\f046"; } 150 | .icon-move:before { content: "\f047"; } 151 | .icon-step-backward:before { content: "\f048"; } 152 | .icon-fast-backward:before { content: "\f049"; } 153 | .icon-backward:before { content: "\f04a"; } 154 | .icon-play:before { content: "\f04b"; } 155 | .icon-pause:before { content: "\f04c"; } 156 | .icon-stop:before { content: "\f04d"; } 157 | .icon-forward:before { content: "\f04e"; } 158 | 159 | .icon-fast-forward:before { content: "\f050"; } 160 | .icon-step-forward:before { content: "\f051"; } 161 | .icon-eject:before { content: "\f052"; } 162 | .icon-chevron-left:before { content: "\f053"; } 163 | .icon-chevron-right:before { content: "\f054"; } 164 | .icon-plus-sign:before { content: "\f055"; } 165 | .icon-minus-sign:before { content: "\f056"; } 166 | .icon-remove-sign:before { content: "\f057"; } 167 | .icon-ok-sign:before { content: "\f058"; } 168 | .icon-question-sign:before { content: "\f059"; } 169 | .icon-info-sign:before { content: "\f05a"; } 170 | .icon-screenshot:before { content: "\f05b"; } 171 | .icon-remove-circle:before { content: "\f05c"; } 172 | .icon-ok-circle:before { content: "\f05d"; } 173 | .icon-ban-circle:before { content: "\f05e"; } 174 | 175 | .icon-arrow-left:before { content: "\f060"; } 176 | .icon-arrow-right:before { content: "\f061"; } 177 | .icon-arrow-up:before { content: "\f062"; } 178 | .icon-arrow-down:before { content: "\f063"; } 179 | .icon-share-alt:before { content: "\f064"; } 180 | .icon-resize-full:before { content: "\f065"; } 181 | .icon-resize-small:before { content: "\f066"; } 182 | .icon-plus:before { content: "\f067"; } 183 | .icon-minus:before { content: "\f068"; } 184 | .icon-asterisk:before { content: "\f069"; } 185 | .icon-exclamation-sign:before { content: "\f06a"; } 186 | .icon-gift:before { content: "\f06b"; } 187 | .icon-leaf:before { content: "\f06c"; } 188 | .icon-fire:before { content: "\f06d"; } 189 | .icon-eye-open:before { content: "\f06e"; } 190 | 191 | .icon-eye-close:before { content: "\f070"; } 192 | .icon-warning-sign:before { content: "\f071"; } 193 | .icon-plane:before { content: "\f072"; } 194 | .icon-calendar:before { content: "\f073"; } 195 | .icon-random:before { content: "\f074"; } 196 | .icon-comment:before { content: "\f075"; } 197 | .icon-magnet:before { content: "\f076"; } 198 | .icon-chevron-up:before { content: "\f077"; } 199 | .icon-chevron-down:before { content: "\f078"; } 200 | .icon-retweet:before { content: "\f079"; } 201 | .icon-shopping-cart:before { content: "\f07a"; } 202 | .icon-folder-close:before { content: "\f07b"; } 203 | .icon-folder-open:before { content: "\f07c"; } 204 | .icon-resize-vertical:before { content: "\f07d"; } 205 | .icon-resize-horizontal:before { content: "\f07e"; } 206 | 207 | .icon-bar-chart:before { content: "\f080"; } 208 | .icon-twitter-sign:before { content: "\f081"; } 209 | .icon-facebook-sign:before { content: "\f082"; } 210 | .icon-camera-retro:before { content: "\f083"; } 211 | .icon-key:before { content: "\f084"; } 212 | .icon-cogs:before { content: "\f085"; } 213 | .icon-comments:before { content: "\f086"; } 214 | .icon-thumbs-up:before { content: "\f087"; } 215 | .icon-thumbs-down:before { content: "\f088"; } 216 | .icon-star-half:before { content: "\f089"; } 217 | .icon-heart-empty:before { content: "\f08a"; } 218 | .icon-signout:before { content: "\f08b"; } 219 | .icon-linkedin-sign:before { content: "\f08c"; } 220 | .icon-pushpin:before { content: "\f08d"; } 221 | .icon-external-link:before { content: "\f08e"; } 222 | 223 | .icon-signin:before { content: "\f090"; } 224 | .icon-trophy:before { content: "\f091"; } 225 | .icon-github-sign:before { content: "\f092"; } 226 | .icon-upload-alt:before { content: "\f093"; } 227 | .icon-lemon:before { content: "\f094"; } 228 | .icon-phone:before { content: "\f095"; } 229 | .icon-check-empty:before { content: "\f096"; } 230 | .icon-bookmark-empty:before { content: "\f097"; } 231 | .icon-phone-sign:before { content: "\f098"; } 232 | .icon-twitter:before { content: "\f099"; } 233 | .icon-facebook:before { content: "\f09a"; } 234 | .icon-github:before { content: "\f09b"; } 235 | .icon-unlock:before { content: "\f09c"; } 236 | .icon-credit-card:before { content: "\f09d"; } 237 | .icon-rss:before { content: "\f09e"; } 238 | 239 | .icon-hdd:before { content: "\f0a0"; } 240 | .icon-bullhorn:before { content: "\f0a1"; } 241 | .icon-bell:before { content: "\f0a2"; } 242 | .icon-certificate:before { content: "\f0a3"; } 243 | .icon-hand-right:before { content: "\f0a4"; } 244 | .icon-hand-left:before { content: "\f0a5"; } 245 | .icon-hand-up:before { content: "\f0a6"; } 246 | .icon-hand-down:before { content: "\f0a7"; } 247 | .icon-circle-arrow-left:before { content: "\f0a8"; } 248 | .icon-circle-arrow-right:before { content: "\f0a9"; } 249 | .icon-circle-arrow-up:before { content: "\f0aa"; } 250 | .icon-circle-arrow-down:before { content: "\f0ab"; } 251 | .icon-globe:before { content: "\f0ac"; } 252 | .icon-wrench:before { content: "\f0ad"; } 253 | .icon-tasks:before { content: "\f0ae"; } 254 | 255 | .icon-filter:before { content: "\f0b0"; } 256 | .icon-briefcase:before { content: "\f0b1"; } 257 | .icon-fullscreen:before { content: "\f0b2"; } 258 | 259 | .icon-group:before { content: "\f0c0"; } 260 | .icon-link:before { content: "\f0c1"; } 261 | .icon-cloud:before { content: "\f0c2"; } 262 | .icon-beaker:before { content: "\f0c3"; } 263 | .icon-cut:before { content: "\f0c4"; } 264 | .icon-copy:before { content: "\f0c5"; } 265 | .icon-paper-clip:before { content: "\f0c6"; } 266 | .icon-save:before { content: "\f0c7"; } 267 | .icon-sign-blank:before { content: "\f0c8"; } 268 | .icon-reorder:before { content: "\f0c9"; } 269 | .icon-list-ul:before { content: "\f0ca"; } 270 | .icon-list-ol:before { content: "\f0cb"; } 271 | .icon-strikethrough:before { content: "\f0cc"; } 272 | .icon-underline:before { content: "\f0cd"; } 273 | .icon-table:before { content: "\f0ce"; } 274 | 275 | .icon-magic:before { content: "\f0d0"; } 276 | .icon-truck:before { content: "\f0d1"; } 277 | .icon-pinterest:before { content: "\f0d2"; } 278 | .icon-pinterest-sign:before { content: "\f0d3"; } 279 | .icon-google-plus-sign:before { content: "\f0d4"; } 280 | .icon-google-plus:before { content: "\f0d5"; } 281 | .icon-money:before { content: "\f0d6"; } 282 | .icon-caret-down:before { content: "\f0d7"; } 283 | .icon-caret-up:before { content: "\f0d8"; } 284 | .icon-caret-left:before { content: "\f0d9"; } 285 | .icon-caret-right:before { content: "\f0da"; } 286 | .icon-columns:before { content: "\f0db"; } 287 | .icon-sort:before { content: "\f0dc"; } 288 | .icon-sort-down:before { content: "\f0dd"; } 289 | .icon-sort-up:before { content: "\f0de"; } 290 | 291 | .icon-envelope-alt:before { content: "\f0e0"; } 292 | .icon-linkedin:before { content: "\f0e1"; } 293 | .icon-undo:before { content: "\f0e2"; } 294 | .icon-legal:before { content: "\f0e3"; } 295 | .icon-dashboard:before { content: "\f0e4"; } 296 | .icon-comment-alt:before { content: "\f0e5"; } 297 | .icon-comments-alt:before { content: "\f0e6"; } 298 | .icon-bolt:before { content: "\f0e7"; } 299 | .icon-sitemap:before { content: "\f0e8"; } 300 | .icon-umbrella:before { content: "\f0e9"; } 301 | .icon-paste:before { content: "\f0ea"; } 302 | 303 | .icon-user-md:before { content: "\f200"; } 304 | -------------------------------------------------------------------------------- /css/graph.css: -------------------------------------------------------------------------------- 1 | /* graph */ 2 | 3 | .rickshaw_graph { 4 | position: relative; 5 | } 6 | .rickshaw_graph svg { 7 | display: block; 8 | overflow: hidden; 9 | } 10 | 11 | /* ticks */ 12 | 13 | .rickshaw_graph .x_tick { 14 | position: absolute; 15 | top: 0; 16 | bottom: 0; 17 | width: 0px; 18 | border-left: 1px dotted rgba(0, 0, 0, 0.2); 19 | pointer-events: none; 20 | } 21 | .rickshaw_graph .x_tick .title { 22 | position: absolute; 23 | font-size: 12px; 24 | font-family: Arial, sans-serif; 25 | opacity: 0.5; 26 | white-space: nowrap; 27 | margin-left: 3px; 28 | bottom: -20px; 29 | } 30 | 31 | /* annotations */ 32 | 33 | .rickshaw_annotation_timeline { 34 | height: 1px; 35 | border-top: 1px solid #e0e0e0; 36 | margin-top: 10px; 37 | position: absolute; 38 | } 39 | .rickshaw_annotation_timeline .annotation { 40 | position: absolute; 41 | height: 6px; 42 | width: 6px; 43 | margin-left: -2px; 44 | top: -3px; 45 | border-radius: 5px; 46 | background-color: rgba(0, 0, 0, 0.25); 47 | } 48 | .rickshaw_graph .annotation_line { 49 | position: absolute; 50 | top: 0; 51 | bottom: -6px; 52 | width: 0px; 53 | border-left: 2px solid rgba(0, 0, 0, 0.3); 54 | display: none; 55 | } 56 | .rickshaw_graph .annotation_line.active { 57 | display: block; 58 | } 59 | 60 | .rickshaw_graph .annotation_range { 61 | background: rgba(0, 0, 0, 0.1); 62 | display: none; 63 | position: absolute; 64 | top: 0; 65 | bottom: -6px; 66 | z-index: -10; 67 | } 68 | .rickshaw_graph .annotation_range.active { 69 | display: block; 70 | } 71 | .rickshaw_graph .annotation_range.active.offscreen { 72 | display: none; 73 | } 74 | 75 | .rickshaw_annotation_timeline .annotation .content { 76 | background: white; 77 | color: black; 78 | opacity: 0.9; 79 | padding: 5px 5px; 80 | box-shadow: 0 0 2px rgba(0, 0, 0, 0.8); 81 | border-radius: 3px; 82 | position: relative; 83 | z-index: 20; 84 | font-size: 12px; 85 | padding: 6px 8px 8px; 86 | top: 18px; 87 | left: -11px; 88 | width: 160px; 89 | display: none; 90 | cursor: pointer; 91 | } 92 | .rickshaw_annotation_timeline .annotation .content:before { 93 | content: "\25b2"; 94 | position: absolute; 95 | top: -11px; 96 | color: white; 97 | text-shadow: 0 -1px 1px rgba(0, 0, 0, 0.8); 98 | } 99 | .rickshaw_annotation_timeline .annotation.active, 100 | .rickshaw_annotation_timeline .annotation:hover { 101 | background-color: rgba(0, 0, 0, 0.8); 102 | cursor: none; 103 | } 104 | .rickshaw_annotation_timeline .annotation .content:hover { 105 | z-index: 50; 106 | } 107 | .rickshaw_annotation_timeline .annotation.active .content { 108 | display: block; 109 | } 110 | .rickshaw_annotation_timeline .annotation:hover .content { 111 | display: block; 112 | z-index: 50; 113 | } 114 | .rickshaw_graph .y_axis { 115 | fill: none; 116 | } 117 | .rickshaw_graph .y_ticks .tick { 118 | stroke: rgba(0, 0, 0, 0.16); 119 | stroke-width: 2px; 120 | shape-rendering: crisp-edges; 121 | pointer-events: none; 122 | } 123 | .rickshaw_graph .y_grid .tick { 124 | z-index: -1; 125 | stroke: rgba(0, 0, 0, 0.20); 126 | stroke-width: 1px; 127 | stroke-dasharray: 1 1; 128 | } 129 | .rickshaw_graph .y_grid path { 130 | fill: none; 131 | stroke: none; 132 | } 133 | .rickshaw_graph .y_ticks path { 134 | fill: none; 135 | stroke: #808080; 136 | } 137 | .rickshaw_graph .y_ticks text { 138 | opacity: 0.5; 139 | font-size: 12px; 140 | pointer-events: none; 141 | } 142 | .rickshaw_graph .x_tick.glow .title, 143 | .rickshaw_graph .y_ticks.glow text { 144 | fill: black; 145 | color: black; 146 | text-shadow: 147 | -1px 1px 0 rgba(255, 255, 255, 0.1), 148 | 1px -1px 0 rgba(255, 255, 255, 0.1), 149 | 1px 1px 0 rgba(255, 255, 255, 0.1), 150 | 0px 1px 0 rgba(255, 255, 255, 0.1), 151 | 0px -1px 0 rgba(255, 255, 255, 0.1), 152 | 1px 0px 0 rgba(255, 255, 255, 0.1), 153 | -1px 0px 0 rgba(255, 255, 255, 0.1), 154 | -1px -1px 0 rgba(255, 255, 255, 0.1); 155 | } 156 | .rickshaw_graph .x_tick.inverse .title, 157 | .rickshaw_graph .y_ticks.inverse text { 158 | fill: white; 159 | color: white; 160 | text-shadow: 161 | -1px 1px 0 rgba(0, 0, 0, 0.8), 162 | 1px -1px 0 rgba(0, 0, 0, 0.8), 163 | 1px 1px 0 rgba(0, 0, 0, 0.8), 164 | 0px 1px 0 rgba(0, 0, 0, 0.8), 165 | 0px -1px 0 rgba(0, 0, 0, 0.8), 166 | 1px 0px 0 rgba(0, 0, 0, 0.8), 167 | -1px 0px 0 rgba(0, 0, 0, 0.8), 168 | -1px -1px 0 rgba(0, 0, 0, 0.8); 169 | } 170 | -------------------------------------------------------------------------------- /css/legend.css: -------------------------------------------------------------------------------- 1 | .rickshaw_legend { 2 | font-family: Arial; 3 | font-size: 12px; 4 | color: white; 5 | background: #404040; 6 | display: inline-block; 7 | padding: 12px 5px; 8 | border-radius: 2px; 9 | position: relative; 10 | } 11 | .rickshaw_legend:hover { 12 | z-index: 10; 13 | } 14 | .rickshaw_legend .swatch { 15 | width: 10px; 16 | height: 10px; 17 | border: 1px solid rgba(0, 0, 0, 0.2); 18 | } 19 | .rickshaw_legend .line { 20 | clear: both; 21 | line-height: 140%; 22 | padding-right: 15px; 23 | } 24 | .rickshaw_legend .line .swatch { 25 | display: inline-block; 26 | margin-right: 3px; 27 | border-radius: 2px; 28 | } 29 | .rickshaw_legend .label { 30 | margin: 0; 31 | white-space: nowrap; 32 | display: inline; 33 | font-size: inherit; 34 | background-color: transparent; 35 | color: inherit; 36 | font-weight: normal; 37 | line-height: normal; 38 | padding: 0px; 39 | text-shadow: none; 40 | } 41 | .rickshaw_legend .action:hover { 42 | opacity: 0.6; 43 | } 44 | .rickshaw_legend .action { 45 | margin-right: 0.2em; 46 | font-size: 10px; 47 | opacity: 0.2; 48 | cursor: pointer; 49 | font-size: 14px; 50 | } 51 | .rickshaw_legend .line.disabled { 52 | opacity: 0.4; 53 | } 54 | .rickshaw_legend ul { 55 | list-style-type: none; 56 | margin: 0; 57 | padding: 0; 58 | margin: 2px; 59 | cursor: pointer; 60 | } 61 | .rickshaw_legend li { 62 | padding: 0 0 0 2px; 63 | min-width: 80px; 64 | white-space: nowrap; 65 | } 66 | .rickshaw_legend li:hover { 67 | background: rgba(255, 255, 255, 0.08); 68 | border-radius: 3px; 69 | } 70 | .rickshaw_legend li:active { 71 | background: rgba(255, 255, 255, 0.2); 72 | border-radius: 3px; 73 | } 74 | -------------------------------------------------------------------------------- /css/main.css: -------------------------------------------------------------------------------- 1 | /* ========================================================================== 2 | Giraffe custom styles 3 | ========================================================================== */ 4 | 5 | body { 6 | background: url("../img/giraffe-bg.png") fixed center center; 7 | padding-top: 40px; 8 | padding-bottom: 0; 9 | } 10 | 11 | body > .container { 12 | background-color: rgba(255, 255, 255, 0.7); 13 | padding: 0 20px 20px; 14 | -webkit-oborder-radius: 0 0 8px 8px; 15 | -moz-border-radius: 0 0 8px 8px; 16 | border-radius: 0 0 8px 8px; 17 | } 18 | 19 | #giraffe-navbar-inner { 20 | background-color: #fafafa; 21 | background-image: -moz-linear-gradient(top, #ffb100, #d65100); 22 | background-image: -ms-linear-gradient(top, #ffb100, #d65100); 23 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffb100), to(#d65100)); 24 | background-image: -webkit-linear-gradient(top, #ffb100, #d65100); 25 | background-image: -o-linear-gradient(top, #ffb100, #d65100); 26 | background-image: linear-gradient(top, #ffb100, #d65100); 27 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#'ffb100, endColorstr='#'d65100, GradientType=0); 28 | -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); 29 | -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); 30 | box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); 31 | } 32 | 33 | .navbar .brand { 34 | /* color: #d65100; */ 35 | color: white; 36 | text-shadow: none; 37 | } 38 | 39 | .navbar .brand:hover { 40 | text-decoration: none; 41 | color: white; 42 | } 43 | 44 | .navbar .nav > li > a { 45 | /* color: #d65100; */ 46 | color: white; 47 | text-shadow: none; 48 | text-decoration: none; 49 | } 50 | 51 | .dropdown-menu { 52 | max-height: 500px; 53 | overflow-y: auto; 54 | overflow-x: hidden; 55 | } 56 | 57 | .navbar-inverse .nav li.dropdown > .dropdown-toggle .caret { 58 | border-top-color: white; 59 | border-bottom-color: white; 60 | } 61 | 62 | .navbar-inverse .navbar-inner { 63 | border-color: #d65100; 64 | } 65 | 66 | .navbar-inverse .nav li.dropdown.open > .dropdown-toggle, .navbar-inverse .nav li.dropdown.active > .dropdown-toggle, .navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle { 67 | color: #d65100; 68 | background-color: white; 69 | } 70 | 71 | .btn-small, .btn-small.active { 72 | color: #d65100; 73 | } 74 | 75 | .page-header h1 { 76 | color: #d65100; 77 | } 78 | p{ 79 | margin-top: 12px; 80 | } 81 | h2 { 82 | font-size: 24.5px; 83 | } 84 | 85 | h2, h3 { 86 | color: #9a5f00; 87 | } 88 | 89 | .page-header { 90 | margin: 0px 0 30px; 91 | padding-top: 15px; 92 | } 93 | 94 | .dashboard-content { 95 | padding-right: 20px; 96 | padding-left: 20px; 97 | } 98 | 99 | .dashboard-content, 100 | #graphs { 101 | background-color: white; 102 | box-shadow: 0 0 10px rgba(255, 255, 255, 1); 103 | border-radius: 0 0 8px 8px; 104 | } 105 | .legend { 106 | margin-top: 17px; 107 | display: none; 108 | } 109 | 110 | .nav-collapse .btn-toolbar { 111 | margin-top: 0px; 112 | margin-bottom: 0px; 113 | } 114 | /* .rickshaw_graph .detail .x_label { */ 115 | /* display: none; */ 116 | /* } */ 117 | /* .rickshaw_graph .detail .item.active { */ 118 | /* opacity:1; */ 119 | /* display: none; */ 120 | /* } */ 121 | -------------------------------------------------------------------------------- /dashboards.js: -------------------------------------------------------------------------------- 1 | var graphite_url = "demo"; // enter your graphite url, e.g. http://your.graphite.com 2 | 3 | var dashboards = 4 | [ 5 | { "name": "Demo", // give your dashboard a name (required!) 6 | "refresh": 5000, // each dashboard has its own refresh interval (in ms) 7 | // add an (optional) dashboard description. description can be written in markdown / html. 8 | "description": "#Giraffe : A [Graphite](http://graphite.wikidot.com) Dashboard with a long neck " 9 | +"\n" 10 | +"\n
" 11 | +"\n View project on github" 12 | +"\n" 13 | +"\n##More?" 14 | +"\n" 15 | +"\nCheck out the different dashboards for more information about riding giraffes." 16 | , 17 | "metrics": // metrics is an array of charts on the dashboard 18 | [ 19 | { 20 | "alias": "signups", // display name for this metric 21 | "target": "sumSeries(enter.your.graphite.metrics.here)", // enter your graphite barebone target expression here 22 | "description": "New signups to the website", // enter your metric description here 23 | "summary": "sum", // available options: [sum|min|max|avg|last|] 24 | "summary_formatter": d3.format(",f") // customize your summary format function. see d3 formatting docs for more options 25 | // also supported are tick_formatter to customize y axis ticks, and totals_formatter to format the values in the legend 26 | }, 27 | { 28 | "alias": "signup breakdown", 29 | "targets": ["sumSeries(enter.your.graphite.metrics.here)", // targets array is also supported 30 | "sumSeries(enter.another.graphite.metrics)"], // see below for more advanced usage 31 | "description": "signup breakdown based on site location", 32 | "renderer": "area", // use any rickshaw-supported renderer 33 | "unstack": true // other parameters like unstack, interpolation, stroke, min, height are also available (see rickshaw documentation for more info) 34 | }, 35 | { 36 | "alias": "Registration breakdown", 37 | "target": "sumSeries(enter.your.graphite.metrics.here)", 38 | // target can use a javascript function. This allows using dynamic parameters (e.g. period). See a few functions 39 | // at the bottom of this file. 40 | "target": function() { return 'summarize(events.registration.success,"' + entire_period() + 'min)' }, 41 | "renderer": "bar", 42 | "description": "Registrations based on channel", 43 | "hover_formatter": d3.format("03.6g"), // customize your hover format 44 | "null_as": 0 // null values are normally ignored, but you can convert null to a specific value (usually zero) 45 | }, 46 | ] 47 | }, 48 | { "name": "Visuals", 49 | "refresh": 10000, 50 | // you can use any rickshaw supported color scheme. 51 | // Enter palette name as string, or an array of colors 52 | // (see var scheme at the bottom). 53 | // Schemes can be configured globally (see below), per-dashboard, or per-metric 54 | "scheme": "classic9", // this is a dashboard-specific color palette 55 | "description": "#Visual settings " 56 | +"\n" 57 | +"\nGiraffe is using the [Rickshaw](http://code.shutterstock.com/rickshaw/) d3 charting library." 58 | +"\nYou can therefore configure almost any visual aspect supported by rickshaw/d3 inside giraffe." 59 | +"\n" 60 | +"\nThis includes: [color scheme](https://github.com/shutterstock/rickshaw#rickshawcolorpalette), interpolation, [renderer](https://github.com/shutterstock/rickshaw#renderer) and more." 61 | +"\n" 62 | +"\nEach graph can span over 1,2 or 3 columns using the `colspan` metric parameter." 63 | +"\n" 64 | +"\n##Top panel" 65 | +"\n" 66 | +"\nThe top panel allows toggling between time ranges (not visible on the demo, but should work fine with graphite)." 67 | +"\nIt also supports toggling the legend , grid " 68 | +"\n, X labels and tooltips " 69 | +"\n" 70 | +"\n## Legend" 71 | +"\n" 72 | +"\nClicking on the legend will show the legend under each chart. The legend includes summary information for each series, " 73 | +"\n Σ: Total; : Minimum; : Maximum; and : Average" 74 | +"\n" 75 | +"\n##Summary" 76 | +"\n" 77 | +"\nIn addition to the legend, each chart can have one `summary` value displayed next to its title." 78 | +"\nThe summary is calculated over the entire series and can be one of `[sum|max|min|avg|last|]`" 79 | +"\n" 80 | +"\n" 81 | +"\n" 82 | , 83 | "metrics": 84 | [ 85 | { 86 | "alias": "network", 87 | "target": "aliasByNode(derivative(servers.system.eth*),4)", 88 | "events": "*", // instead of annotator, if you use the graphite events feature 89 | // you can retrieve events matching specific tag(s) -- space separated 90 | // or use * for all tags. Note you cannot use both annotator and events. 91 | "description": "main system cpu usage on production (cardinal interpolation, line renderer, colspan=3)", 92 | "interpolation": "linear", 93 | "colspan": 3, 94 | }, 95 | { 96 | "alias": "cpu utilization", 97 | "target": "aliasByNode(derivative(servers.system.cpu.*),4)", // target can use any graphite-supported wildcards 98 | "annotator": 'events.deployment', // a simple annotator will track a graphite event and mark it as 'deployment'. 99 | // enter your graphite target as a string 100 | "description": "cpu utilization on production (using linear interpolation). Summary displays the average across all series", 101 | "interpolation": "linear", // you can use different rickshaw interpolation values 102 | "stroke_width": 1, // change stroke width 103 | "stroke": stroke, // stoke may be true, false or a function that takes and returns a d3 rgb color to style the stroke 104 | "summary": "avg", 105 | "totals_fields": ["min", "max", "avg"], // customize which totals are shown in the legend 106 | "summary_formatter": format_pct, 107 | "hover_formatter": format_pct, 108 | "totals_formatter": format_pct, // customize the formatting of the legend totals 109 | "tick_formatter": format_pct, // and also the y axis ticks 110 | }, 111 | { 112 | "alias": "proc mem prod", 113 | "targets": ["aliasByNode(derivative(servers.system.cpu.user),4)", // targets array can include strings, 114 | // functions or dictionaries 115 | // an example of a target object 116 | {target: 'alias(derivative(servers.system.cpu.system,"system utilization")', 117 | alias: 'system utilization', // this alias *must* match the graphite alias. 118 | color: '#f00'}], // you can also specify a target color this way 119 | // (note that these values are ignored on the demo) 120 | // annotator can also be a dictionary of target and description. 121 | // However, only one annotator is supported per-metric. 122 | "annotator": {'target' : 'events.deployment', 123 | 'description' : 'deploy'}, 124 | "description": "main process memory usage on production (different colour scheme and interpolation)", 125 | "interpolation": "step-before", 126 | "scheme": "munin", // this is a metric-specific color palette 127 | }, 128 | { 129 | "alias": "sys mem prod", 130 | "target": "aliasByNode(derivative(servers.system.cpu.*),4)", 131 | "events": "*", // instead of annotator, if you use the graphite events feature 132 | // you can retrieve events matching specific tag(s) -- space separated 133 | // or use * for all tags. Note you cannot use both annotator and events. 134 | "description": "main system memory usage on production (cardinal interpolation, line renderer)", 135 | "interpolation": "cardinal", 136 | "renderer": "line", 137 | "max": 150, // you can specify max value for the y-axis 138 | "min": 20, // and also min 139 | }, 140 | { 141 | "alias": "System Load (Multi Renderer)", 142 | // an example of a multi renderer. When renderer is set to multi 143 | // each target can have its own renderer. E.g. to mix between a line and bar charts. 144 | "renderer": 'multi', 145 | "target": "aliasByNode(derivative(servers.system.cpu.*),4)", // target can use any graphite-supported wildcards 146 | "targets": [ 147 | {'target': 'aliasByNode(servers.server06.system.load.load,1)', 148 | 'color': '#A99', 'renderer': 'bar', 'alias': 'server06'}, 149 | {'target': 150 | 'alias(movingAverage(servers.server06.system.load.load,"-5min"),' + 151 | '"Moving Average of 5 segments")', 'color': '#F00', 152 | 'alias': 'Moving Average of 5 segments', 153 | 'renderer': 'line'} 154 | ], 155 | "interpolation": "linear", 156 | "description": "multi renderer (line and bars on the same chart)", 157 | "annotator": {'target' : 'events.deployment', 158 | 'description' : 'deploy'}, 159 | "max": 150, 160 | "colspan": 3 161 | }, 162 | ] 163 | }, 164 | { "name": "Setup", 165 | "refresh": 10000, 166 | "scheme": "colorwheel", 167 | "graphite_url": "demo", // you can override the default graphite_url with a dashboard-specific url 168 | "description": "#Setup and configuration " 169 | +"\n" 170 | +"\n##Installation" 171 | +"\n" 172 | +"\nTo install giraffe, simply [download](https://github.com/kenhub/giraffe/archive/master.zip) the code and run it from your browser." 173 | +"\nYou can put it on any type of web server, and also open the `index.html` file from your local drive." 174 | +"\n" 175 | +"\n##Authentication" 176 | +"\n" 177 | +"\nGiraffe uses JSONP to retrieve the data from your graphite server. It should work out of the box, unless you" 178 | +"\nhave setup authentication. Basic authentication seems to work in Firefox (it will prompt you), " 179 | +"\nbut with Chrome you might need to authenticate to your graphite server first, and then access Giraffe." 180 | +"\n" 181 | +"\n##Configuration" 182 | +"\n" 183 | +"\nThe main configuration for all dashboards is found in `dashboards.js`. The file is reasonably self-explanatory, " 184 | +"\nso please take a look." 185 | +"\n" 186 | +"\nIf you need to change the page layout, CSS, or add/remove a time period, you can also edit `index.html` and `css/main.css` file." 187 | +"\n" 188 | , 189 | "metrics": 190 | [ 191 | { 192 | "alias": "production HTTP req", 193 | "target": "aliasByNode(derivative(servers.gluteus-medius.Http.http_response_rates.*),4)", 194 | "renderer": "bar", 195 | "interpolation": "cardinal", 196 | "summary": "last", 197 | }, 198 | ] 199 | }, 200 | ]; 201 | 202 | var scheme = [ 203 | '#423d4f', 204 | '#4a6860', 205 | '#848f39', 206 | '#a2b73c', 207 | '#ddcb53', 208 | '#c5a32f', 209 | '#7d5836', 210 | '#963b20', 211 | '#7c2626', 212 | ].reverse(); 213 | 214 | function relative_period() { return (typeof period == 'undefined') ? 1 : parseInt(period / 7) + 1; } 215 | function entire_period() { return (typeof period == 'undefined') ? 1 : period; } 216 | function at_least_a_day() { return entire_period() >= 1440 ? entire_period() : 1440; } 217 | 218 | function stroke(color) { return color.brighter().brighter() } 219 | function format_pct(n) { return d3.format(",f")(n) + "%" } 220 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenhub/giraffe/a70b125985ac7390aa13589385dc4638288c7238/favicon.ico -------------------------------------------------------------------------------- /font/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenhub/giraffe/a70b125985ac7390aa13589385dc4638288c7238/font/fontawesome-webfont.eot -------------------------------------------------------------------------------- /font/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenhub/giraffe/a70b125985ac7390aa13589385dc4638288c7238/font/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /font/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenhub/giraffe/a70b125985ac7390aa13589385dc4638288c7238/font/fontawesome-webfont.woff -------------------------------------------------------------------------------- /img/giraffe-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenhub/giraffe/a70b125985ac7390aa13589385dc4638288c7238/img/giraffe-bg.png -------------------------------------------------------------------------------- /img/giraffe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenhub/giraffe/a70b125985ac7390aa13589385dc4638288c7238/img/giraffe.png -------------------------------------------------------------------------------- /img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenhub/giraffe/a70b125985ac7390aa13589385dc4638288c7238/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenhub/giraffe/a70b125985ac7390aa13589385dc4638288c7238/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 65 | 66 |
67 |
68 | 69 |
70 |
71 | 72 |
73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /js/giraffe-annotator.js: -------------------------------------------------------------------------------- 1 | // GiraffeAnnotate is a modified version of Rickshaw.Graph.Annotate 2 | // which doesn't hide annotations that are off-screen 3 | // this is necessary to show graphite events that might be outside 4 | // the target time range 5 | GiraffeAnnotate = function(args) { 6 | 7 | var graph = this.graph = args.graph; 8 | this.elements = { timeline: args.element }; 9 | 10 | var self = this; 11 | 12 | this.data = {}; 13 | 14 | this.elements.timeline.classList.add('rickshaw_annotation_timeline'); 15 | 16 | this.add = function(time, content, end_time) { 17 | self.data[time] = self.data[time] || {'boxes': []}; 18 | self.data[time].boxes.push({content: content, end: end_time}); 19 | }; 20 | 21 | this.update = function() { 22 | 23 | Rickshaw.keys(self.data).forEach( function(time) { 24 | 25 | var annotation = self.data[time]; 26 | var left = self.graph.x(time); 27 | 28 | // if (left < 0 || left > self.graph.x.range()[1]) { 29 | // if (annotation.element) { 30 | // annotation.line.classList.add('offscreen'); 31 | // annotation.element.style.display = 'none'; 32 | // } 33 | 34 | // annotation.boxes.forEach( function(box) { 35 | // if ( box.rangeElement ) box.rangeElement.classList.add('offscreen'); 36 | // }); 37 | 38 | // return; 39 | // } 40 | 41 | if (!annotation.element) { 42 | var element = annotation.element = document.createElement('div'); 43 | element.classList.add('annotation'); 44 | this.elements.timeline.appendChild(element); 45 | element.addEventListener('click', function(e) { 46 | element.classList.toggle('active'); 47 | annotation.line.classList.toggle('active'); 48 | annotation.boxes.forEach( function(box) { 49 | if ( box.rangeElement ) box.rangeElement.classList.toggle('active'); 50 | }); 51 | }, false); 52 | 53 | } 54 | 55 | annotation.element.style.left = left + 'px'; 56 | annotation.element.style.display = 'block'; 57 | 58 | annotation.boxes.forEach( function(box) { 59 | 60 | 61 | var element = box.element; 62 | 63 | if (!element) { 64 | element = box.element = document.createElement('div'); 65 | element.classList.add('content'); 66 | element.innerHTML = box.content; 67 | annotation.element.appendChild(element); 68 | 69 | annotation.line = document.createElement('div'); 70 | annotation.line.classList.add('annotation_line'); 71 | self.graph.element.appendChild(annotation.line); 72 | 73 | if ( box.end ) { 74 | box.rangeElement = document.createElement('div'); 75 | box.rangeElement.classList.add('annotation_range'); 76 | self.graph.element.appendChild(box.rangeElement); 77 | } 78 | 79 | } 80 | 81 | if ( box.end ) { 82 | 83 | var annotationRangeStart = left; 84 | var annotationRangeEnd = Math.min( self.graph.x(box.end), self.graph.x.range()[1] ); 85 | 86 | // annotation makes more sense at end 87 | if ( annotationRangeStart > annotationRangeEnd ) { 88 | annotationRangeEnd = left; 89 | annotationRangeStart = Math.max( self.graph.x(box.end), self.graph.x.range()[0] ); 90 | } 91 | 92 | var annotationRangeWidth = annotationRangeEnd - annotationRangeStart; 93 | 94 | box.rangeElement.style.left = annotationRangeStart + 'px'; 95 | box.rangeElement.style.width = annotationRangeWidth + 'px' 96 | 97 | box.rangeElement.classList.remove('offscreen'); 98 | } 99 | 100 | annotation.line.classList.remove('offscreen'); 101 | annotation.line.style.left = left + 'px'; 102 | } ); 103 | }, this ); 104 | }; 105 | 106 | this.graph.onUpdate( function() { self.update() } ); 107 | }; 108 | -------------------------------------------------------------------------------- /js/giraffe.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var _avg, _formatBase1024KMGTP, _last, _max, _min, _sum, auth, changeDashboard, createGraph, dashboard, dataPoll, default_graphite_url, default_period, description, generateDataURL, generateEventsURL, generateGraphiteTargets, getTargetColor, getTargetRenderer, graphScaffold, graphite_url, graphs, init, metrics, period, refresh, refreshSummary, refreshTimer, scheme, toggleCss, 3 | indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; 4 | 5 | graphite_url = graphite_url || 'demo'; 6 | 7 | default_graphite_url = graphite_url; 8 | 9 | default_period = 1440; 10 | 11 | if (scheme === void 0) { 12 | scheme = 'classic9'; 13 | } 14 | 15 | period = default_period; 16 | 17 | dashboard = dashboards[0]; 18 | 19 | metrics = dashboard['metrics']; 20 | 21 | description = dashboard['description']; 22 | 23 | refresh = dashboard['refresh']; 24 | 25 | refreshTimer = null; 26 | 27 | auth = auth != null ? auth : false; 28 | 29 | graphs = []; 30 | 31 | dataPoll = function() { 32 | var graph, j, len, results; 33 | results = []; 34 | for (j = 0, len = graphs.length; j < len; j++) { 35 | graph = graphs[j]; 36 | results.push(graph.refreshGraph(period)); 37 | } 38 | return results; 39 | }; 40 | 41 | _sum = function(series) { 42 | return _.reduce(series, (function(memo, val) { 43 | return memo + val; 44 | }), 0); 45 | }; 46 | 47 | _avg = function(series) { 48 | return _sum(series) / series.length; 49 | }; 50 | 51 | _max = function(series) { 52 | return _.reduce(series, (function(memo, val) { 53 | if (memo === null) { 54 | return val; 55 | } 56 | if (val > memo) { 57 | return val; 58 | } 59 | return memo; 60 | }), null); 61 | }; 62 | 63 | _min = function(series) { 64 | return _.reduce(series, (function(memo, val) { 65 | if (memo === null) { 66 | return val; 67 | } 68 | if (val < memo) { 69 | return val; 70 | } 71 | return memo; 72 | }), null); 73 | }; 74 | 75 | _last = function(series) { 76 | return _.reduce(series, (function(memo, val) { 77 | if (val !== null) { 78 | return val; 79 | } 80 | return memo; 81 | }), null); 82 | }; 83 | 84 | _formatBase1024KMGTP = function(y, formatter) { 85 | var abs_y; 86 | if (formatter == null) { 87 | formatter = d3.format(".2r"); 88 | } 89 | abs_y = Math.abs(y); 90 | if (abs_y >= 1125899906842624) { 91 | return formatter(y / 1125899906842624) + "P"; 92 | } else if (abs_y >= 1099511627776) { 93 | return formatter(y / 1099511627776) + "T"; 94 | } else if (abs_y >= 1073741824) { 95 | return formatter(y / 1073741824) + "G"; 96 | } else if (abs_y >= 1048576) { 97 | return formatter(y / 1048576) + "M"; 98 | } else if (abs_y >= 1024) { 99 | return formatter(y / 1024) + "K"; 100 | } else if (abs_y < 1 && y > 0) { 101 | return formatter(y); 102 | } else if (abs_y === 0) { 103 | return 0; 104 | } else { 105 | return formatter(y); 106 | } 107 | }; 108 | 109 | refreshSummary = function(graph) { 110 | var ref, summary_func, y_data; 111 | if (!((ref = graph.args) != null ? ref.summary : void 0)) { 112 | return; 113 | } 114 | if (graph.args.summary === "sum") { 115 | summary_func = _sum; 116 | } 117 | if (graph.args.summary === "avg") { 118 | summary_func = _avg; 119 | } 120 | if (graph.args.summary === "min") { 121 | summary_func = _min; 122 | } 123 | if (graph.args.summary === "max") { 124 | summary_func = _max; 125 | } 126 | if (graph.args.summary === "last") { 127 | summary_func = _last; 128 | } 129 | if (typeof graph.args.summary === "function") { 130 | summary_func = graph.args.summary; 131 | } 132 | if (!summary_func) { 133 | console.log("unknown summary function " + graph.args.summary); 134 | } 135 | y_data = _.map(_.flatten(_.pluck(graph.graph.series, 'data')), function(d) { 136 | return d.y; 137 | }); 138 | return $(graph.args.anchor + " .graph-summary").html(graph.args.summary_formatter(summary_func(y_data))); 139 | }; 140 | 141 | graphScaffold = function() { 142 | var colspan, context, converter, graph_template, i, j, len, metric, offset; 143 | graph_template = "{{#dashboard_description}}\n
{{{dashboard_description}}}
\n{{/dashboard_description}}\n{{#metrics}}\n {{#start_row}}\n
\n {{/start_row}}\n
\n

{{metric_alias}}

\n
\n
\n

{{metric_description}}

\n
\n
\n {{#end_row}}\n
\n {{/end_row}}\n{{/metrics}}"; 144 | $('#graphs').empty(); 145 | context = { 146 | metrics: [] 147 | }; 148 | converter = new Markdown.Converter(); 149 | if (description) { 150 | context['dashboard_description'] = converter.makeHtml(description); 151 | } 152 | offset = 0; 153 | for (i = j = 0, len = metrics.length; j < len; i = ++j) { 154 | metric = metrics[i]; 155 | colspan = metric.colspan != null ? metric.colspan : 1; 156 | context['metrics'].push({ 157 | start_row: offset % 3 === 0, 158 | end_row: offset % 3 === 2, 159 | graph_id: i, 160 | span: 'span' + (4 * colspan), 161 | metric_alias: metric.alias, 162 | metric_description: metric.description 163 | }); 164 | offset += colspan; 165 | } 166 | return $('#graphs').append(Mustache.render(graph_template, context)); 167 | }; 168 | 169 | init = function() { 170 | var dash, i, j, k, len, len1, metric, refreshInterval; 171 | $('.dropdown-menu').empty(); 172 | for (j = 0, len = dashboards.length; j < len; j++) { 173 | dash = dashboards[j]; 174 | $('.dropdown-menu').append("
  • " + dash.name + "
  • "); 175 | } 176 | graphScaffold(); 177 | graphs = []; 178 | for (i = k = 0, len1 = metrics.length; k < len1; i = ++k) { 179 | metric = metrics[i]; 180 | graphs.push(createGraph("#graph-" + i, metric)); 181 | } 182 | $('.page-header h1').empty().append(dashboard.name); 183 | refreshInterval = refresh || 10000; 184 | if (refreshTimer) { 185 | clearInterval(refreshTimer); 186 | } 187 | return refreshTimer = setInterval(dataPoll, refreshInterval); 188 | }; 189 | 190 | getTargetColor = function(targets, target) { 191 | var j, len, t; 192 | if (typeof targets !== 'object') { 193 | return; 194 | } 195 | for (j = 0, len = targets.length; j < len; j++) { 196 | t = targets[j]; 197 | if (!t.color) { 198 | continue; 199 | } 200 | if (t.target === target || t.alias === target) { 201 | return t.color; 202 | } 203 | } 204 | }; 205 | 206 | getTargetRenderer = function(targets, target) { 207 | var j, len, t; 208 | if (typeof targets !== 'object') { 209 | return; 210 | } 211 | for (j = 0, len = targets.length; j < len; j++) { 212 | t = targets[j]; 213 | if (!t.renderer) { 214 | continue; 215 | } 216 | if (t.target === target || t.alias === target) { 217 | return t.renderer; 218 | } 219 | } 220 | }; 221 | 222 | generateGraphiteTargets = function(targets) { 223 | var graphite_targets, j, len, target; 224 | if (typeof targets === "string") { 225 | return "&target=" + targets; 226 | } 227 | if (typeof targets === "function") { 228 | return "&target=" + (targets()); 229 | } 230 | graphite_targets = ""; 231 | for (j = 0, len = targets.length; j < len; j++) { 232 | target = targets[j]; 233 | if (typeof target === "string") { 234 | graphite_targets += "&target=" + target; 235 | } 236 | if (typeof target === "function") { 237 | graphite_targets += "&target=" + (target()); 238 | } 239 | if (typeof target === "object") { 240 | graphite_targets += "&target=" + ((target != null ? target.target : void 0) || ''); 241 | } 242 | } 243 | return graphite_targets; 244 | }; 245 | 246 | generateDataURL = function(targets, annotator_target, max_data_points) { 247 | var data_targets; 248 | annotator_target = annotator_target ? "&target=" + annotator_target : ""; 249 | data_targets = generateGraphiteTargets(targets); 250 | return graphite_url + "/render?from=-" + period + "minutes&" + data_targets + annotator_target + "&maxDataPoints=" + max_data_points + "&format=json&jsonp=?"; 251 | }; 252 | 253 | generateEventsURL = function(event_tags) { 254 | var jsonp, tags; 255 | tags = event_tags === '*' ? '' : "&tags=" + event_tags; 256 | jsonp = window.json_fallback ? '' : "&jsonp=?"; 257 | return graphite_url + "/events/get_data?from=-" + period + "minutes" + tags + jsonp; 258 | }; 259 | 260 | createGraph = function(anchor, metric) { 261 | var graph, graph_provider, ref, ref1, ref2, unstackable; 262 | if (graphite_url === 'demo') { 263 | graph_provider = Rickshaw.Graph.Demo; 264 | } else { 265 | graph_provider = Rickshaw.Graph.JSONP.Graphite; 266 | } 267 | unstackable = (ref = metric.renderer) === 'line' || ref === 'scatterplot'; 268 | return graph = new graph_provider({ 269 | anchor: anchor, 270 | targets: metric.target || metric.targets, 271 | summary: metric.summary, 272 | summary_formatter: metric.summary_formatter || _formatBase1024KMGTP, 273 | totals_formatter: metric.totals_formatter || _formatBase1024KMGTP, 274 | totals_fields: metric.totals_fields || ["sum", "min", "max", "avg"], 275 | scheme: metric.scheme || dashboard.scheme || scheme || 'classic9', 276 | annotator_target: ((ref1 = metric.annotator) != null ? ref1.target : void 0) || metric.annotator, 277 | annotator_description: ((ref2 = metric.annotator) != null ? ref2.description : void 0) || 'deployment', 278 | events: metric.events, 279 | element: $(anchor + " .chart")[0], 280 | width: metric.width || $(anchor + " .chart").width() || 300, 281 | height: metric.height || 300, 282 | min: metric.min === void 0 ? 'auto' : metric.min, 283 | max: metric.max, 284 | null_as: metric.null_as === void 0 ? null : metric.null_as, 285 | renderer: metric.renderer || 'area', 286 | interpolation: metric.interpolation || 'step-before', 287 | offset: metric.offset || 'zero', 288 | unstack: metric.unstack === void 0 ? unstackable : metric.unstack, 289 | stroke: metric.stroke === false ? false : true, 290 | stroke_fn: typeof metric.stroke === "function" ? metric.stroke : void 0, 291 | strokeWidth: metric.stroke_width, 292 | dataURL: generateDataURL(metric.target || metric.targets), 293 | onRefresh: function(transport) { 294 | return refreshSummary(transport); 295 | }, 296 | onComplete: function(transport) { 297 | var detail, hover_formatter, shelving, xAxis, yAxis; 298 | graph = transport.graph; 299 | xAxis = new Rickshaw.Graph.Axis.Time({ 300 | graph: graph 301 | }); 302 | xAxis.render(); 303 | yAxis = new Rickshaw.Graph.Axis.Y({ 304 | graph: graph, 305 | tickFormat: metric.tick_formatter || function(y) { 306 | return _formatBase1024KMGTP(y); 307 | }, 308 | ticksTreatment: 'glow' 309 | }); 310 | yAxis.render(); 311 | hover_formatter = metric.hover_formatter || _formatBase1024KMGTP; 312 | detail = new Rickshaw.Graph.HoverDetail({ 313 | graph: graph, 314 | yFormatter: function(y) { 315 | return hover_formatter(y); 316 | } 317 | }); 318 | $(anchor + " .legend").empty(); 319 | this.legend = new Rickshaw.Graph.Legend({ 320 | graph: graph, 321 | element: $(anchor + " .legend")[0] 322 | }); 323 | shelving = new Rickshaw.Graph.Behavior.Series.Toggle({ 324 | graph: graph, 325 | legend: this.legend 326 | }); 327 | if (metric.annotator || metric.events) { 328 | this.annotator = new GiraffeAnnotate({ 329 | graph: graph, 330 | element: $(anchor + " .timeline")[0] 331 | }); 332 | } 333 | return refreshSummary(this); 334 | } 335 | }); 336 | }; 337 | 338 | Rickshaw.Graph.JSONP.Graphite = Rickshaw.Class.create(Rickshaw.Graph.JSONP, { 339 | request: function() { 340 | return this.refreshGraph(period); 341 | }, 342 | refreshGraph: function(period) { 343 | var deferred; 344 | deferred = this.getAjaxData(period); 345 | return deferred.done((function(_this) { 346 | return function(result) { 347 | var annotations, el, i, j, len, result_data, series; 348 | if (result.length <= 0) { 349 | return; 350 | } 351 | result_data = _.filter(result, function(el) { 352 | var ref; 353 | return el.target !== ((ref = _this.args.annotator_target) != null ? ref.replace(/["']/g, '') : void 0); 354 | }); 355 | result_data = _this.preProcess(result_data); 356 | if (!_this.graph) { 357 | _this.success(_this.parseGraphiteData(result_data, _this.args.null_as)); 358 | } 359 | series = _this.parseGraphiteData(result_data, _this.args.null_as); 360 | if (_this.args.annotator_target) { 361 | annotations = _this.parseGraphiteData(_.filter(result, function(el) { 362 | return el.target === _this.args.annotator_target.replace(/["']/g, ''); 363 | }), _this.args.null_as); 364 | } 365 | for (i = j = 0, len = series.length; j < len; i = ++j) { 366 | el = series[i]; 367 | _this.graph.series[i].data = el.data; 368 | _this.addTotals(i); 369 | } 370 | _this.graph.renderer.unstack = _this.args.unstack; 371 | _this.graph.render(); 372 | if (_this.args.events) { 373 | deferred = _this.getEvents(period); 374 | deferred.done(function(result) { 375 | return _this.addEventAnnotations(result); 376 | }); 377 | } 378 | _this.addAnnotations(annotations, _this.args.annotator_description); 379 | return _this.args.onRefresh(_this); 380 | }; 381 | })(this)); 382 | }, 383 | addTotals: function(i) { 384 | var avg, label, max, min, series_data, sum, totals; 385 | label = $(this.legend.lines[i].element).find('span.label').text(); 386 | $(this.legend.lines[i].element).find('span.totals').remove(); 387 | series_data = _.map(this.legend.lines[i].series.data, function(d) { 388 | return d.y; 389 | }); 390 | sum = this.args.totals_formatter(_sum(series_data)); 391 | max = this.args.totals_formatter(_max(series_data)); 392 | min = this.args.totals_formatter(_min(series_data)); 393 | avg = this.args.totals_formatter(_avg(series_data)); 394 | totals = ""; 395 | if (indexOf.call(this.args.totals_fields, "sum") >= 0) { 396 | totals = totals + (" Σ: " + sum); 397 | } 398 | if (indexOf.call(this.args.totals_fields, "min") >= 0) { 399 | totals = totals + (" : " + min); 400 | } 401 | if (indexOf.call(this.args.totals_fields, "max") >= 0) { 402 | totals = totals + (" : " + max); 403 | } 404 | if (indexOf.call(this.args.totals_fields, "avg") >= 0) { 405 | totals = totals + (" : " + avg); 406 | } 407 | totals += ""; 408 | return $(this.legend.lines[i].element).append(totals); 409 | }, 410 | preProcess: function(result) { 411 | var item, j, len; 412 | for (j = 0, len = result.length; j < len; j++) { 413 | item = result[j]; 414 | if (item.datapoints.length === 1) { 415 | item.datapoints[0][1] = 0; 416 | if (this.args.unstack) { 417 | item.datapoints.push([0, 1]); 418 | } else { 419 | item.datapoints.push([item.datapoints[0][0], 1]); 420 | } 421 | } 422 | } 423 | return result; 424 | }, 425 | parseGraphiteData: function(d, null_as) { 426 | var palette, rev_xy, stroke_fn, targets; 427 | if (null_as == null) { 428 | null_as = null; 429 | } 430 | rev_xy = function(datapoints) { 431 | return _.map(datapoints, function(point) { 432 | return { 433 | 'x': point[1], 434 | 'y': point[0] !== null ? point[0] : null_as 435 | }; 436 | }); 437 | }; 438 | palette = new Rickshaw.Color.Palette({ 439 | scheme: this.args.scheme 440 | }); 441 | targets = this.args.target || this.args.targets; 442 | stroke_fn = this.args.stroke_fn; 443 | d = _.map(d, function(el) { 444 | var color, ref, renderer; 445 | if ((ref = typeof targets) === "string" || ref === "function") { 446 | color = palette.color(); 447 | } else { 448 | color = getTargetColor(targets, el.target) || palette.color(); 449 | renderer = getTargetRenderer(targets, el.target) || 'line'; 450 | } 451 | return { 452 | color: color, 453 | renderer: renderer, 454 | stroke: stroke_fn != null ? stroke_fn(d3.rgb(color)) : void 0, 455 | name: el.target, 456 | data: rev_xy(el.datapoints) 457 | }; 458 | }); 459 | Rickshaw.Series.zeroFill(d); 460 | return d; 461 | }, 462 | addEventAnnotations: function(events_json) { 463 | var active_annotation, event, j, len, ref, ref1; 464 | if (!events_json) { 465 | return; 466 | } 467 | this.annotator || (this.annotator = new GiraffeAnnotate({ 468 | graph: this.graph, 469 | element: $(this.args.anchor + " .timeline")[0] 470 | })); 471 | this.annotator.data = {}; 472 | $(this.annotator.elements.timeline).empty(); 473 | active_annotation = $(this.annotator.elements.timeline).parent().find('.annotation_line.active').size() > 0; 474 | if ((ref = $(this.annotator.elements.timeline).parent()) != null) { 475 | ref.find('.annotation_line').remove(); 476 | } 477 | for (j = 0, len = events_json.length; j < len; j++) { 478 | event = events_json[j]; 479 | this.annotator.add(event.when, event.what + " " + (event.data || '')); 480 | } 481 | this.annotator.update(); 482 | if (active_annotation) { 483 | return (ref1 = $(this.annotator.elements.timeline).parent()) != null ? ref1.find('.annotation_line').addClass('active') : void 0; 484 | } 485 | }, 486 | addAnnotations: function(annotations, description) { 487 | var annotation_timestamps, ref; 488 | if (!annotations) { 489 | return; 490 | } 491 | annotation_timestamps = _((ref = annotations[0]) != null ? ref.data : void 0).filter(function(el) { 492 | return el.y !== 0 && el.y !== null; 493 | }); 494 | return this.addEventAnnotations(_.map(annotation_timestamps, function(a) { 495 | return { 496 | when: a.x, 497 | what: description 498 | }; 499 | })); 500 | }, 501 | getEvents: function(period) { 502 | var deferred; 503 | this.period = period; 504 | return deferred = $.ajax({ 505 | dataType: 'json', 506 | url: generateEventsURL(this.args.events), 507 | error: (function(_this) { 508 | return function(xhr, textStatus, errorThrown) { 509 | if (textStatus === 'parsererror' && /was not called/.test(errorThrown.message)) { 510 | window.json_fallback = true; 511 | return _this.refreshGraph(period); 512 | } else { 513 | return console.log("error loading eventsURL: " + generateEventsURL(_this.args.events)); 514 | } 515 | }; 516 | })(this) 517 | }); 518 | }, 519 | getAjaxData: function(period) { 520 | var deferred; 521 | this.period = period; 522 | return deferred = $.ajax({ 523 | dataType: 'json', 524 | url: generateDataURL(this.args.targets, this.args.annotator_target, this.args.width), 525 | error: this.error.bind(this) 526 | }); 527 | } 528 | }); 529 | 530 | Rickshaw.Graph.Demo = Rickshaw.Class.create(Rickshaw.Graph.JSONP.Graphite, { 531 | success: function(data) { 532 | var i, j, palette; 533 | palette = new Rickshaw.Color.Palette({ 534 | scheme: this.args.scheme 535 | }); 536 | this.seriesData = [[], [], [], [], [], [], [], [], []]; 537 | this.random = new Rickshaw.Fixtures.RandomData(period / 60 + 10); 538 | for (i = j = 0; j <= 60; i = ++j) { 539 | this.random.addData(this.seriesData); 540 | } 541 | this.graph = new Rickshaw.Graph({ 542 | element: this.args.element, 543 | width: this.args.width, 544 | height: this.args.height, 545 | min: this.args.min, 546 | max: this.args.max, 547 | renderer: this.args.renderer, 548 | interpolation: this.args.interpolation, 549 | stroke: this.args.stroke, 550 | strokeWidth: this.args.strokeWidth, 551 | series: [ 552 | { 553 | color: palette.color(), 554 | data: this.seriesData[0], 555 | name: 'Moscow', 556 | renderer: 'line' 557 | }, { 558 | color: palette.color(), 559 | data: this.seriesData[1], 560 | name: 'Shanghai', 561 | renderer: 'line' 562 | }, { 563 | color: palette.color(), 564 | data: this.seriesData[2], 565 | name: 'Amsterdam', 566 | renderer: 'bar' 567 | }, { 568 | color: palette.color(), 569 | data: this.seriesData[3], 570 | name: 'Paris', 571 | renderer: 'line' 572 | }, { 573 | color: palette.color(), 574 | data: this.seriesData[4], 575 | name: 'Tokyo', 576 | renderer: 'line' 577 | }, { 578 | color: palette.color(), 579 | data: this.seriesData[5], 580 | name: 'London', 581 | renderer: 'line' 582 | }, { 583 | color: palette.color(), 584 | data: this.seriesData[6], 585 | name: 'New York', 586 | renderer: 'line' 587 | } 588 | ].map((function(_this) { 589 | return function(s) { 590 | if (_this.args.stroke_fn != null) { 591 | s.stroke = _this.args.stroke_fn(d3.rgb(s.color)); 592 | } 593 | return s; 594 | }; 595 | })(this)) 596 | }); 597 | this.graph.renderer.unstack = this.args.unstack; 598 | this.graph.render(); 599 | return this.onComplete(this); 600 | }, 601 | refreshGraph: function(period) { 602 | var i, j, ref, results; 603 | if (!this.graph) { 604 | return this.success(); 605 | } else { 606 | this.random.addData(this.seriesData); 607 | this.random.addData(this.seriesData); 608 | _.each(this.seriesData, function(d) { 609 | return d.shift(); 610 | }); 611 | this.args.onRefresh(this); 612 | this.graph.render(); 613 | results = []; 614 | for (i = j = 0, ref = this.graph.series.length; 0 <= ref ? j < ref : j > ref; i = 0 <= ref ? ++j : --j) { 615 | results.push(this.addTotals(i)); 616 | } 617 | return results; 618 | } 619 | } 620 | }); 621 | 622 | 623 | /* 624 | * Events and interaction 625 | */ 626 | 627 | $('.dropdown-menu').on('click', 'a', function() { 628 | changeDashboard($(this).text()); 629 | $('.dropdown').removeClass('open'); 630 | return false; 631 | }); 632 | 633 | changeDashboard = function(dash_name) { 634 | dashboard = _.where(dashboards, { 635 | name: dash_name 636 | })[0] || dashboards[0]; 637 | graphite_url = dashboard['graphite_url'] || default_graphite_url; 638 | description = dashboard['description']; 639 | metrics = dashboard['metrics']; 640 | refresh = dashboard['refresh']; 641 | period || (period = default_period); 642 | init(); 643 | return $.bbq.pushState({ 644 | dashboard: dashboard.name 645 | }); 646 | }; 647 | 648 | $('.timepanel').on('click', 'a.range', function() { 649 | var dash, ref, timeFrame; 650 | if (graphite_url === 'demo') { 651 | changeDashboard(dashboard.name); 652 | } 653 | period = $(this).attr('data-timeframe') || default_period; 654 | dataPoll(); 655 | timeFrame = $(this).attr('href').replace(/^#/, ''); 656 | dash = (ref = $.bbq.getState()) != null ? ref.dashboard : void 0; 657 | $.bbq.pushState({ 658 | timeFrame: timeFrame, 659 | dashboard: dash || dashboard.name 660 | }); 661 | $(this).parent('.btn-group').find('a').removeClass('active'); 662 | $(this).addClass('active'); 663 | return false; 664 | }); 665 | 666 | toggleCss = function(css_selector) { 667 | if ($.rule(css_selector).text().match('display: ?none')) { 668 | return $.rule(css_selector, 'style').remove(); 669 | } else { 670 | return $.rule(css_selector + " {display:none;}").appendTo('style'); 671 | } 672 | }; 673 | 674 | $('#legend-toggle').on('click', function() { 675 | $(this).toggleClass('active'); 676 | $('.legend').toggle(); 677 | return false; 678 | }); 679 | 680 | $('#axis-toggle').on('click', function() { 681 | $(this).toggleClass('active'); 682 | toggleCss('.y_grid'); 683 | toggleCss('.y_ticks'); 684 | toggleCss('.x_tick'); 685 | return false; 686 | }); 687 | 688 | $('#x-label-toggle').on('click', function() { 689 | toggleCss('.rickshaw_graph .detail .x_label'); 690 | $(this).toggleClass('active'); 691 | return false; 692 | }); 693 | 694 | $('#x-item-toggle').on('click', function() { 695 | toggleCss('.rickshaw_graph .detail .item.active'); 696 | $(this).toggleClass('active'); 697 | return false; 698 | }); 699 | 700 | $(window).bind('hashchange', function(e) { 701 | var dash, ref, ref1, timeFrame; 702 | timeFrame = ((ref = e.getState()) != null ? ref.timeFrame : void 0) || $(".timepanel a.range[data-timeframe='" + default_period + "']")[0].text || "1d"; 703 | dash = (ref1 = e.getState()) != null ? ref1.dashboard : void 0; 704 | if (dash !== dashboard.name) { 705 | changeDashboard(dash); 706 | } 707 | return $('.timepanel a.range[href="#' + timeFrame + '"]').click(); 708 | }); 709 | 710 | $(function() { 711 | $(window).trigger('hashchange'); 712 | return init(); 713 | }); 714 | -------------------------------------------------------------------------------- /js/src/giraffe.coffee: -------------------------------------------------------------------------------- 1 | # giraffe vars 2 | graphite_url = graphite_url || 'demo' 3 | default_graphite_url = graphite_url 4 | default_period = 1440 5 | scheme = 'classic9' if scheme is undefined 6 | period = default_period 7 | dashboard = dashboards[0] 8 | metrics = dashboard['metrics'] 9 | description = dashboard['description'] 10 | refresh = dashboard['refresh'] 11 | refreshTimer = null 12 | auth = auth ? false 13 | graphs = [] 14 | 15 | dataPoll = -> 16 | for graph in graphs 17 | graph.refreshGraph(period) 18 | 19 | # helper functions 20 | _sum = (series) -> 21 | _.reduce(series, ((memo, val) -> 22 | memo + val) 23 | ,0) 24 | 25 | _avg = (series) -> 26 | _sum(series)/series.length 27 | 28 | _max = (series) -> 29 | _.reduce(series, ((memo, val) -> 30 | return val if memo is null 31 | return val if val > memo 32 | return memo) 33 | ,null) 34 | 35 | _min = (series) -> 36 | _.reduce(series, ((memo, val) -> 37 | return val if memo is null 38 | return val if val < memo 39 | return memo) 40 | ,null) 41 | 42 | _last = (series) -> 43 | _.reduce(series, ((memo, val) -> 44 | return val if val != null 45 | return memo) 46 | ,null) 47 | 48 | _formatBase1024KMGTP = (y, formatter = d3.format(".2r")) -> 49 | abs_y = Math.abs(y) 50 | if abs_y >= 1125899906842624 then return formatter(y / 1125899906842624) + "P" 51 | else if abs_y >= 1099511627776 then return formatter(y / 1099511627776) + "T" 52 | else if abs_y >= 1073741824 then return formatter(y / 1073741824) + "G" 53 | else if abs_y >= 1048576 then return formatter(y / 1048576) + "M" 54 | else if abs_y >= 1024 then return formatter(y / 1024) + "K" 55 | else if abs_y < 1 && y > 0 then return formatter(y) 56 | else if abs_y == 0 then return 0 57 | else return formatter(y) 58 | 59 | # updates the graph summary value (if any) 60 | # summary options: [sum|avg|min|max|last|] 61 | refreshSummary = (graph) -> 62 | return unless graph.args?.summary 63 | summary_func = _sum if graph.args.summary is "sum" 64 | summary_func = _avg if graph.args.summary is "avg" 65 | summary_func = _min if graph.args.summary is "min" 66 | summary_func = _max if graph.args.summary is "max" 67 | summary_func = _last if graph.args.summary is "last" 68 | summary_func = graph.args.summary if typeof graph.args.summary is "function" 69 | console.log("unknown summary function #{graph.args.summary}") unless summary_func 70 | y_data = _.map(_.flatten(_.pluck(graph.graph.series, 'data')), (d) -> d.y) 71 | $("#{graph.args.anchor} .graph-summary").html(graph.args.summary_formatter(summary_func(y_data))) 72 | 73 | 74 | # builds the HTML scaffolding for the graphs 75 | # using a small mustache template 76 | graphScaffold = -> 77 | graph_template = """ 78 | {{#dashboard_description}} 79 |
    {{{dashboard_description}}}
    80 | {{/dashboard_description}} 81 | {{#metrics}} 82 | {{#start_row}} 83 |
    84 | {{/start_row}} 85 |
    86 |

    {{metric_alias}}

    87 |
    88 |
    89 |

    {{metric_description}}

    90 |
    91 |
    92 | {{#end_row}} 93 |
    94 | {{/end_row}} 95 | {{/metrics}}""" 96 | 97 | $('#graphs').empty() 98 | context = {metrics: []} 99 | converter = new Markdown.Converter() 100 | context['dashboard_description'] = converter.makeHtml(description) if description 101 | offset = 0 102 | for metric, i in metrics 103 | colspan = if metric.colspan? then metric.colspan else 1 104 | context['metrics'].push 105 | start_row: offset % 3 is 0 106 | end_row: offset % 3 is 2 107 | graph_id: i 108 | span: 'span' + (4 * colspan) 109 | metric_alias: metric.alias 110 | metric_description: metric.description 111 | offset += colspan 112 | $('#graphs').append Mustache.render(graph_template, context) 113 | 114 | init = -> 115 | $('.dropdown-menu').empty() 116 | for dash in dashboards 117 | $('.dropdown-menu').append("
  • #{dash.name}
  • ") 118 | 119 | graphScaffold() 120 | 121 | graphs = [] 122 | for metric, i in metrics 123 | graphs.push createGraph("#graph-#{i}", metric) 124 | $('.page-header h1').empty().append(dashboard.name) 125 | # auto refresh 126 | refreshInterval = refresh || 10000 127 | clearInterval(refreshTimer) if refreshTimer 128 | refreshTimer = setInterval(dataPoll, refreshInterval) 129 | 130 | getTargetColor = (targets, target) -> 131 | return unless typeof targets is 'object' 132 | for t in targets 133 | continue unless t.color 134 | if t.target == target or t.alias == target 135 | return t.color 136 | 137 | getTargetRenderer = (targets, target) -> 138 | return unless typeof targets is 'object' 139 | for t in targets 140 | continue unless t.renderer 141 | if t.target == target or t.alias == target 142 | return t.renderer 143 | 144 | generateGraphiteTargets = (targets) -> 145 | # checking if single target (string) or a function 146 | if typeof targets is "string" then return "&target=#{targets}" 147 | if typeof targets is "function" then return "&target=#{targets()}" 148 | # handling multiple targets 149 | graphite_targets = "" 150 | for target in targets 151 | graphite_targets += "&target=#{target}" if typeof target is "string" 152 | graphite_targets += "&target=#{target()}" if typeof target is "function" 153 | graphite_targets += "&target=#{target?.target || ''}" if typeof target is "object" 154 | return graphite_targets 155 | 156 | # generate a URL to retrieve data from graphite 157 | generateDataURL= (targets, annotator_target, max_data_points) -> 158 | annotator_target = if annotator_target then "&target=#{annotator_target}" else "" 159 | data_targets = generateGraphiteTargets(targets) 160 | "#{graphite_url}/render?from=-#{period}minutes&#{data_targets}#{annotator_target}&maxDataPoints=#{max_data_points}&format=json&jsonp=?" 161 | 162 | # generate a URL to retrieve events from graphite 163 | generateEventsURL= (event_tags) -> 164 | tags = if event_tags is '*' then '' else "&tags=#{event_tags}" 165 | jsonp = if window.json_fallback then '' else "&jsonp=?" 166 | "#{graphite_url}/events/get_data?from=-#{period}minutes#{tags}#{jsonp}" 167 | 168 | 169 | # builds a graph object 170 | createGraph = (anchor, metric) -> 171 | 172 | if graphite_url == 'demo' 173 | graph_provider = Rickshaw.Graph.Demo 174 | else 175 | graph_provider = Rickshaw.Graph.JSONP.Graphite 176 | unstackable = metric.renderer in ['line', 'scatterplot'] 177 | graph = new graph_provider 178 | anchor: anchor 179 | targets: metric.target || metric.targets 180 | summary: metric.summary 181 | summary_formatter: metric.summary_formatter || _formatBase1024KMGTP 182 | totals_formatter: metric.totals_formatter || _formatBase1024KMGTP 183 | totals_fields: metric.totals_fields || ["sum", "min", "max", "avg"] 184 | scheme: metric.scheme || dashboard.scheme || scheme || 'classic9' 185 | annotator_target: metric.annotator?.target || metric.annotator 186 | annotator_description: metric.annotator?.description || 'deployment' 187 | events: metric.events 188 | element: $("#{anchor} .chart")[0] 189 | width: metric.width || $("#{anchor} .chart").width() || 300 190 | height: metric.height || 300 191 | min: if metric.min is undefined then 'auto' else metric.min 192 | max: metric.max 193 | null_as: if metric.null_as is undefined then null else metric.null_as 194 | renderer: metric.renderer || 'area' 195 | interpolation: metric.interpolation || 'step-before' 196 | offset: metric.offset || 'zero' 197 | unstack: if metric.unstack is undefined then unstackable else metric.unstack 198 | stroke: if metric.stroke is false then false else true 199 | stroke_fn: metric.stroke if typeof metric.stroke is "function" 200 | strokeWidth: metric.stroke_width 201 | dataURL: generateDataURL(metric.target || metric.targets) 202 | onRefresh: (transport) -> 203 | refreshSummary(transport) 204 | onComplete: (transport) -> 205 | graph = transport.graph 206 | # graph.onUpdate(addAnotations) 207 | xAxis = new Rickshaw.Graph.Axis.Time 208 | graph: graph 209 | xAxis.render() 210 | yAxis = new Rickshaw.Graph.Axis.Y 211 | graph: graph 212 | tickFormat: metric.tick_formatter || (y) -> _formatBase1024KMGTP(y) 213 | ticksTreatment: 'glow' 214 | yAxis.render() 215 | # element: $("#{anchor} .y-axis")[0] 216 | hover_formatter = metric.hover_formatter || _formatBase1024KMGTP 217 | detail = new Rickshaw.Graph.HoverDetail 218 | graph: graph 219 | yFormatter: (y) -> hover_formatter(y) 220 | # a bit of an ugly hack, but some times onComplete 221 | # seems to be called twice, generating duplicate legend 222 | $("#{anchor} .legend").empty() 223 | @legend = new Rickshaw.Graph.Legend 224 | graph: graph 225 | element: $("#{anchor} .legend")[0] 226 | shelving = new Rickshaw.Graph.Behavior.Series.Toggle 227 | graph: graph 228 | legend: @legend 229 | if metric.annotator or metric.events 230 | @annotator = new GiraffeAnnotate 231 | graph: graph 232 | element: $("#{anchor} .timeline")[0] 233 | refreshSummary(@) 234 | 235 | 236 | Rickshaw.Graph.JSONP.Graphite = Rickshaw.Class.create(Rickshaw.Graph.JSONP, 237 | request: -> 238 | @refreshGraph(period) 239 | 240 | refreshGraph: (period) -> 241 | 242 | deferred = @getAjaxData(period) 243 | deferred.done (result) => 244 | return if result.length <= 0 245 | result_data = _.filter(result, (el) => 246 | el.target != @args.annotator_target?.replace(/["']/g, '')) 247 | result_data = @preProcess(result_data) 248 | # success is called once to build the initial graph 249 | @success(@parseGraphiteData(result_data, @args.null_as)) if not @graph 250 | series = @parseGraphiteData(result_data, @args.null_as) 251 | annotations = @parseGraphiteData(_.filter(result, (el) => 252 | el.target == @args.annotator_target.replace(/["']/g, '')), @args.null_as) if @args.annotator_target 253 | for el, i in series 254 | @graph.series[i].data = el.data 255 | @addTotals(i) 256 | @graph.renderer.unstack = @args.unstack 257 | @graph.render() 258 | # adding event annotations if events are specified 259 | if @args.events 260 | deferred = @getEvents(period) 261 | deferred.done (result) => 262 | @addEventAnnotations(result) 263 | @addAnnotations(annotations, @args.annotator_description) 264 | @args.onRefresh(@) 265 | 266 | addTotals: (i) -> 267 | label = $(@legend.lines[i].element).find('span.label').text() 268 | $(@legend.lines[i].element).find('span.totals').remove() 269 | series_data = _.map(@legend.lines[i].series.data, (d) -> d.y) 270 | sum = @args.totals_formatter(_sum(series_data)) 271 | max = @args.totals_formatter(_max(series_data)) 272 | min = @args.totals_formatter(_min(series_data)) 273 | avg = @args.totals_formatter(_avg(series_data)) 274 | 275 | totals = "" 276 | totals = totals + " Σ: #{sum}" if "sum" in @args.totals_fields 277 | totals = totals + " : #{min}" if "min" in @args.totals_fields 278 | totals = totals + " : #{max}" if "max" in @args.totals_fields 279 | totals = totals + " : #{avg}" if "avg" in @args.totals_fields 280 | totals += "" 281 | 282 | $(@legend.lines[i].element).append(totals) 283 | 284 | preProcess: (result) -> 285 | for item in result 286 | # when we get a single datapoint, we need to add another one 287 | # for Rickshaw to draw it properly. 288 | # 289 | # We either add zero value or repeat the same value 290 | # depending on whether the graph is stacked or not 291 | if item.datapoints.length == 1 292 | item.datapoints[0][1] = 0 293 | if @args.unstack 294 | item.datapoints.push [0, 1] 295 | else 296 | item.datapoints.push [item.datapoints[0][0], 1] 297 | result 298 | 299 | # parses graphite data and produces a 300 | # rickshaw series data structure 301 | parseGraphiteData: (d, null_as = null) -> 302 | 303 | rev_xy = (datapoints) -> 304 | _.map datapoints, (point) -> 305 | {'x': point[1], 'y': if point[0] != null then point[0] else null_as} 306 | 307 | palette = new Rickshaw.Color.Palette 308 | scheme: @args.scheme 309 | targets = @args.target || @args.targets 310 | stroke_fn = @args.stroke_fn 311 | d = _.map d, (el) -> 312 | if typeof targets in ["string", "function"] 313 | color = palette.color() 314 | else 315 | color = getTargetColor(targets, el.target) || palette.color() 316 | renderer = getTargetRenderer(targets, el.target) || 'line' 317 | return { 318 | color: color 319 | renderer: renderer 320 | stroke: stroke_fn(d3.rgb(color)) if stroke_fn? 321 | name: el.target 322 | data: rev_xy(el.datapoints) 323 | } 324 | Rickshaw.Series.zeroFill(d) 325 | return d 326 | 327 | addEventAnnotations: (events_json) -> 328 | return unless events_json 329 | @annotator ||= new GiraffeAnnotate 330 | graph: @graph 331 | element: $("#{@args.anchor} .timeline")[0] 332 | 333 | @annotator.data = {} 334 | $(@annotator.elements.timeline).empty() 335 | active_annotation = $(@annotator.elements.timeline) 336 | .parent().find('.annotation_line.active').size() > 0 337 | $(@annotator.elements.timeline).parent()?.find('.annotation_line').remove() 338 | for event in events_json 339 | @annotator.add(event.when, "#{event.what} #{event.data or ''}") 340 | @annotator.update() 341 | if active_annotation 342 | $(@annotator.elements.timeline).parent()?.find('.annotation_line').addClass('active') 343 | 344 | addAnnotations: (annotations, description) -> 345 | return unless annotations 346 | annotation_timestamps = _(annotations[0]?.data).filter (el) -> el.y != 0 and el.y != null 347 | @addEventAnnotations _.map(annotation_timestamps, (a) -> {when: a.x, what: description}) 348 | 349 | getEvents: (period) -> 350 | @period = period 351 | deferred = $.ajax 352 | dataType: 'json' 353 | url: generateEventsURL(@args.events) 354 | error: (xhr, textStatus, errorThrown) => 355 | # trying to fallback to json if jsonp wasn't available 356 | if textStatus is 'parsererror' and /was not called/.test(errorThrown.message) 357 | window.json_fallback = true 358 | @refreshGraph(period) 359 | else 360 | console.log("error loading eventsURL: " + generateEventsURL(@args.events)) 361 | 362 | getAjaxData: (period) -> 363 | @period = period 364 | deferred = $.ajax 365 | dataType: 'json' 366 | url: generateDataURL(@args.targets, @args.annotator_target, @args.width) 367 | error: @error.bind(@) 368 | ) 369 | 370 | Rickshaw.Graph.Demo = Rickshaw.Class.create(Rickshaw.Graph.JSONP.Graphite, 371 | success: (data) -> 372 | palette = new Rickshaw.Color.Palette 373 | scheme: @args.scheme 374 | @seriesData = [ [], [], [], [], [], [], [], [], [] ] 375 | @random = new Rickshaw.Fixtures.RandomData(period/60 + 10) 376 | 377 | for i in [0..60] 378 | @random.addData(@seriesData) 379 | @graph = new Rickshaw.Graph 380 | element: @args.element 381 | width: @args.width 382 | height: @args.height 383 | min: @args.min 384 | max: @args.max 385 | renderer: @args.renderer 386 | interpolation: @args.interpolation 387 | stroke: @args.stroke 388 | strokeWidth: @args.strokeWidth 389 | series: [ 390 | { 391 | color: palette.color(), 392 | data: @seriesData[0], 393 | name: 'Moscow', 394 | renderer: 'line' 395 | }, { 396 | color: palette.color(), 397 | data: @seriesData[1], 398 | name: 'Shanghai', 399 | renderer: 'line' 400 | }, { 401 | color: palette.color(), 402 | data: @seriesData[2], 403 | name: 'Amsterdam', 404 | renderer: 'bar' 405 | }, { 406 | color: palette.color(), 407 | data: @seriesData[3], 408 | name: 'Paris', 409 | renderer: 'line' 410 | }, { 411 | color: palette.color(), 412 | data: @seriesData[4], 413 | name: 'Tokyo', 414 | renderer: 'line' 415 | }, { 416 | color: palette.color(), 417 | data: @seriesData[5], 418 | name: 'London', 419 | renderer: 'line' 420 | }, { 421 | color: palette.color(), 422 | data: @seriesData[6], 423 | name: 'New York', 424 | renderer: 'line' 425 | } 426 | ].map((s) => 427 | s.stroke = @args.stroke_fn(d3.rgb(s.color)) if @args.stroke_fn? 428 | s) 429 | 430 | @graph.renderer.unstack = @args.unstack 431 | @graph.render() 432 | @onComplete(@) 433 | 434 | refreshGraph: (period) -> 435 | if not @graph 436 | @success() 437 | else 438 | @random.addData(@seriesData) 439 | @random.addData(@seriesData) 440 | _.each(@seriesData, (d) -> d.shift()) 441 | @args.onRefresh(@) 442 | @graph.render() 443 | for i in [0...@graph.series.length] 444 | @addTotals(i) 445 | ) 446 | 447 | ### 448 | # Events and interaction 449 | ### 450 | # dashboard selection 451 | $('.dropdown-menu').on 'click', 'a', -> 452 | changeDashboard($(this).text()) 453 | $('.dropdown').removeClass('open') 454 | false 455 | 456 | # changing to a different dashboard 457 | changeDashboard = (dash_name) -> 458 | dashboard = _.where(dashboards, {name: dash_name})[0] || dashboards[0] 459 | graphite_url = dashboard['graphite_url'] || default_graphite_url 460 | description = dashboard['description'] 461 | metrics = dashboard['metrics'] 462 | refresh = dashboard['refresh'] 463 | period ||= default_period 464 | init() 465 | $.bbq.pushState({dashboard: dashboard.name}) 466 | 467 | # time panel - changing timeframe for graphs 468 | $('.timepanel').on 'click', 'a.range', -> 469 | if graphite_url == 'demo' then changeDashboard(dashboard.name) 470 | period = $(this).attr('data-timeframe') || default_period 471 | dataPoll() 472 | timeFrame = $(this).attr('href').replace(/^#/, '') 473 | dash = $.bbq.getState()?.dashboard 474 | $.bbq.pushState({timeFrame: timeFrame, dashboard: dash || dashboard.name}) 475 | $(this).parent('.btn-group').find('a').removeClass('active') 476 | $(this).addClass('active') 477 | false 478 | 479 | # "permanently" add a css style to hide an element 480 | # (useful when elements are refreshed / don't exist yet) 481 | toggleCss = (css_selector) -> 482 | if $.rule(css_selector).text().match('display: ?none') 483 | $.rule(css_selector, 'style').remove() 484 | else 485 | $.rule("#{css_selector} {display:none;}").appendTo('style') 486 | 487 | # toggle legend 488 | $('#legend-toggle').on 'click', -> 489 | $(this).toggleClass('active') 490 | $('.legend').toggle() 491 | false 492 | 493 | # toggle x and y axis display 494 | $('#axis-toggle').on 'click', -> 495 | $(this).toggleClass('active') 496 | toggleCss('.y_grid') 497 | toggleCss('.y_ticks') 498 | toggleCss('.x_tick') 499 | false 500 | 501 | # toggle x labels inside the graphs 502 | $('#x-label-toggle').on 'click', -> 503 | toggleCss('.rickshaw_graph .detail .x_label') 504 | $(this).toggleClass('active') 505 | false 506 | 507 | # toggle active item text display 508 | $('#x-item-toggle').on 'click', -> 509 | toggleCss('.rickshaw_graph .detail .item.active') 510 | $(this).toggleClass('active') 511 | false 512 | 513 | # hashchange allows history for dashboard + timeframe 514 | $(window).bind 'hashchange', (e) -> 515 | timeFrame = e.getState()?.timeFrame || $(".timepanel a.range[data-timeframe='#{default_period}']")[0].text || "1d" 516 | dash = e.getState()?.dashboard 517 | if dash != dashboard.name 518 | changeDashboard(dash) 519 | $('.timepanel a.range[href="#' + timeFrame + '"]').click() 520 | 521 | $ -> 522 | $(window).trigger( 'hashchange' ) 523 | init() 524 | 525 | -------------------------------------------------------------------------------- /js/vendor/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap.js by @fat & @mdo 3 | * Copyright 2012 Twitter, Inc. 4 | * http://www.apache.org/licenses/LICENSE-2.0.txt 5 | */ 6 | !function(e){"use strict";e(function(){e.support.transition=function(){var e=function(){var e=document.createElement("bootstrap"),t={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"},n;for(n in t)if(e.style[n]!==undefined)return t[n]}();return e&&{end:e}}()})}(window.jQuery),!function(e){"use strict";var t='[data-dismiss="alert"]',n=function(n){e(n).on("click",t,this.close)};n.prototype.close=function(t){function s(){i.trigger("closed").remove()}var n=e(this),r=n.attr("data-target"),i;r||(r=n.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,"")),i=e(r),t&&t.preventDefault(),i.length||(i=n.hasClass("alert")?n:n.parent()),i.trigger(t=e.Event("close"));if(t.isDefaultPrevented())return;i.removeClass("in"),e.support.transition&&i.hasClass("fade")?i.on(e.support.transition.end,s):s()},e.fn.alert=function(t){return this.each(function(){var r=e(this),i=r.data("alert");i||r.data("alert",i=new n(this)),typeof t=="string"&&i[t].call(r)})},e.fn.alert.Constructor=n,e(document).on("click.alert.data-api",t,n.prototype.close)}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.button.defaults,n)};t.prototype.setState=function(e){var t="disabled",n=this.$element,r=n.data(),i=n.is("input")?"val":"html";e+="Text",r.resetText||n.data("resetText",n[i]()),n[i](r[e]||this.options[e]),setTimeout(function(){e=="loadingText"?n.addClass(t).attr(t,t):n.removeClass(t).removeAttr(t)},0)},t.prototype.toggle=function(){var e=this.$element.closest('[data-toggle="buttons-radio"]');e&&e.find(".active").removeClass("active"),this.$element.toggleClass("active")},e.fn.button=function(n){return this.each(function(){var r=e(this),i=r.data("button"),s=typeof n=="object"&&n;i||r.data("button",i=new t(this,s)),n=="toggle"?i.toggle():n&&i.setState(n)})},e.fn.button.defaults={loadingText:"loading..."},e.fn.button.Constructor=t,e(document).on("click.button.data-api","[data-toggle^=button]",function(t){var n=e(t.target);n.hasClass("btn")||(n=n.closest(".btn")),n.button("toggle")})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=n,this.options.slide&&this.slide(this.options.slide),this.options.pause=="hover"&&this.$element.on("mouseenter",e.proxy(this.pause,this)).on("mouseleave",e.proxy(this.cycle,this))};t.prototype={cycle:function(t){return t||(this.paused=!1),this.options.interval&&!this.paused&&(this.interval=setInterval(e.proxy(this.next,this),this.options.interval)),this},to:function(t){var n=this.$element.find(".item.active"),r=n.parent().children(),i=r.index(n),s=this;if(t>r.length-1||t<0)return;return this.sliding?this.$element.one("slid",function(){s.to(t)}):i==t?this.pause().cycle():this.slide(t>i?"next":"prev",e(r[t]))},pause:function(t){return t||(this.paused=!0),this.$element.find(".next, .prev").length&&e.support.transition.end&&(this.$element.trigger(e.support.transition.end),this.cycle()),clearInterval(this.interval),this.interval=null,this},next:function(){if(this.sliding)return;return this.slide("next")},prev:function(){if(this.sliding)return;return this.slide("prev")},slide:function(t,n){var r=this.$element.find(".item.active"),i=n||r[t](),s=this.interval,o=t=="next"?"left":"right",u=t=="next"?"first":"last",a=this,f;this.sliding=!0,s&&this.pause(),i=i.length?i:this.$element.find(".item")[u](),f=e.Event("slide",{relatedTarget:i[0]});if(i.hasClass("active"))return;if(e.support.transition&&this.$element.hasClass("slide")){this.$element.trigger(f);if(f.isDefaultPrevented())return;i.addClass(t),i[0].offsetWidth,r.addClass(o),i.addClass(o),this.$element.one(e.support.transition.end,function(){i.removeClass([t,o].join(" ")).addClass("active"),r.removeClass(["active",o].join(" ")),a.sliding=!1,setTimeout(function(){a.$element.trigger("slid")},0)})}else{this.$element.trigger(f);if(f.isDefaultPrevented())return;r.removeClass("active"),i.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return s&&this.cycle(),this}},e.fn.carousel=function(n){return this.each(function(){var r=e(this),i=r.data("carousel"),s=e.extend({},e.fn.carousel.defaults,typeof n=="object"&&n),o=typeof n=="string"?n:s.slide;i||r.data("carousel",i=new t(this,s)),typeof n=="number"?i.to(n):o?i[o]():s.interval&&i.cycle()})},e.fn.carousel.defaults={interval:5e3,pause:"hover"},e.fn.carousel.Constructor=t,e(document).on("click.carousel.data-api","[data-slide]",function(t){var n=e(this),r,i=e(n.attr("data-target")||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,"")),s=e.extend({},i.data(),n.data());i.carousel(s),t.preventDefault()})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.collapse.defaults,n),this.options.parent&&(this.$parent=e(this.options.parent)),this.options.toggle&&this.toggle()};t.prototype={constructor:t,dimension:function(){var e=this.$element.hasClass("width");return e?"width":"height"},show:function(){var t,n,r,i;if(this.transitioning)return;t=this.dimension(),n=e.camelCase(["scroll",t].join("-")),r=this.$parent&&this.$parent.find("> .accordion-group > .in");if(r&&r.length){i=r.data("collapse");if(i&&i.transitioning)return;r.collapse("hide"),i||r.data("collapse",null)}this.$element[t](0),this.transition("addClass",e.Event("show"),"shown"),e.support.transition&&this.$element[t](this.$element[0][n])},hide:function(){var t;if(this.transitioning)return;t=this.dimension(),this.reset(this.$element[t]()),this.transition("removeClass",e.Event("hide"),"hidden"),this.$element[t](0)},reset:function(e){var t=this.dimension();return this.$element.removeClass("collapse")[t](e||"auto")[0].offsetWidth,this.$element[e!==null?"addClass":"removeClass"]("collapse"),this},transition:function(t,n,r){var i=this,s=function(){n.type=="show"&&i.reset(),i.transitioning=0,i.$element.trigger(r)};this.$element.trigger(n);if(n.isDefaultPrevented())return;this.transitioning=1,this.$element[t]("in"),e.support.transition&&this.$element.hasClass("collapse")?this.$element.one(e.support.transition.end,s):s()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}},e.fn.collapse=function(n){return this.each(function(){var r=e(this),i=r.data("collapse"),s=typeof n=="object"&&n;i||r.data("collapse",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.collapse.defaults={toggle:!0},e.fn.collapse.Constructor=t,e(document).on("click.collapse.data-api","[data-toggle=collapse]",function(t){var n=e(this),r,i=n.attr("data-target")||t.preventDefault()||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,""),s=e(i).data("collapse")?"toggle":n.data();n[e(i).hasClass("in")?"addClass":"removeClass"]("collapsed"),e(i).collapse(s)})}(window.jQuery),!function(e){"use strict";function r(){e(t).each(function(){i(e(this)).removeClass("open")})}function i(t){var n=t.attr("data-target"),r;return n||(n=t.attr("href"),n=n&&/#/.test(n)&&n.replace(/.*(?=#[^\s]*$)/,"")),r=e(n),r.length||(r=t.parent()),r}var t="[data-toggle=dropdown]",n=function(t){var n=e(t).on("click.dropdown.data-api",this.toggle);e("html").on("click.dropdown.data-api",function(){n.parent().removeClass("open")})};n.prototype={constructor:n,toggle:function(t){var n=e(this),s,o;if(n.is(".disabled, :disabled"))return;return s=i(n),o=s.hasClass("open"),r(),o||(s.toggleClass("open"),n.focus()),!1},keydown:function(t){var n,r,s,o,u,a;if(!/(38|40|27)/.test(t.keyCode))return;n=e(this),t.preventDefault(),t.stopPropagation();if(n.is(".disabled, :disabled"))return;o=i(n),u=o.hasClass("open");if(!u||u&&t.keyCode==27)return n.click();r=e("[role=menu] li:not(.divider) a",o);if(!r.length)return;a=r.index(r.filter(":focus")),t.keyCode==38&&a>0&&a--,t.keyCode==40&&a').appendTo(document.body),this.$backdrop.click(this.options.backdrop=="static"?e.proxy(this.$element[0].focus,this.$element[0]):e.proxy(this.hide,this)),i&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),i?this.$backdrop.one(e.support.transition.end,t):t()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),e.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(e.support.transition.end,e.proxy(this.removeBackdrop,this)):this.removeBackdrop()):t&&t()}},e.fn.modal=function(n){return this.each(function(){var r=e(this),i=r.data("modal"),s=e.extend({},e.fn.modal.defaults,r.data(),typeof n=="object"&&n);i||r.data("modal",i=new t(this,s)),typeof n=="string"?i[n]():s.show&&i.show()})},e.fn.modal.defaults={backdrop:!0,keyboard:!0,show:!0},e.fn.modal.Constructor=t,e(document).on("click.modal.data-api",'[data-toggle="modal"]',function(t){var n=e(this),r=n.attr("href"),i=e(n.attr("data-target")||r&&r.replace(/.*(?=#[^\s]+$)/,"")),s=i.data("modal")?"toggle":e.extend({remote:!/#/.test(r)&&r},i.data(),n.data());t.preventDefault(),i.modal(s).one("hide",function(){n.focus()})})}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("tooltip",e,t)};t.prototype={constructor:t,init:function(t,n,r){var i,s;this.type=t,this.$element=e(n),this.options=this.getOptions(r),this.enabled=!0,this.options.trigger=="click"?this.$element.on("click."+this.type,this.options.selector,e.proxy(this.toggle,this)):this.options.trigger!="manual"&&(i=this.options.trigger=="hover"?"mouseenter":"focus",s=this.options.trigger=="hover"?"mouseleave":"blur",this.$element.on(i+"."+this.type,this.options.selector,e.proxy(this.enter,this)),this.$element.on(s+"."+this.type,this.options.selector,e.proxy(this.leave,this))),this.options.selector?this._options=e.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},getOptions:function(t){return t=e.extend({},e.fn[this.type].defaults,t,this.$element.data()),t.delay&&typeof t.delay=="number"&&(t.delay={show:t.delay,hide:t.delay}),t},enter:function(t){var n=e(t.currentTarget)[this.type](this._options).data(this.type);if(!n.options.delay||!n.options.delay.show)return n.show();clearTimeout(this.timeout),n.hoverState="in",this.timeout=setTimeout(function(){n.hoverState=="in"&&n.show()},n.options.delay.show)},leave:function(t){var n=e(t.currentTarget)[this.type](this._options).data(this.type);this.timeout&&clearTimeout(this.timeout);if(!n.options.delay||!n.options.delay.hide)return n.hide();n.hoverState="out",this.timeout=setTimeout(function(){n.hoverState=="out"&&n.hide()},n.options.delay.hide)},show:function(){var e,t,n,r,i,s,o;if(this.hasContent()&&this.enabled){e=this.tip(),this.setContent(),this.options.animation&&e.addClass("fade"),s=typeof this.options.placement=="function"?this.options.placement.call(this,e[0],this.$element[0]):this.options.placement,t=/in/.test(s),e.detach().css({top:0,left:0,display:"block"}).insertAfter(this.$element),n=this.getPosition(t),r=e[0].offsetWidth,i=e[0].offsetHeight;switch(t?s.split(" ")[1]:s){case"bottom":o={top:n.top+n.height,left:n.left+n.width/2-r/2};break;case"top":o={top:n.top-i,left:n.left+n.width/2-r/2};break;case"left":o={top:n.top+n.height/2-i/2,left:n.left-r};break;case"right":o={top:n.top+n.height/2-i/2,left:n.left+n.width}}e.offset(o).addClass(s).addClass("in")}},setContent:function(){var e=this.tip(),t=this.getTitle();e.find(".tooltip-inner")[this.options.html?"html":"text"](t),e.removeClass("fade in top bottom left right")},hide:function(){function r(){var t=setTimeout(function(){n.off(e.support.transition.end).detach()},500);n.one(e.support.transition.end,function(){clearTimeout(t),n.detach()})}var t=this,n=this.tip();return n.removeClass("in"),e.support.transition&&this.$tip.hasClass("fade")?r():n.detach(),this},fixTitle:function(){var e=this.$element;(e.attr("title")||typeof e.attr("data-original-title")!="string")&&e.attr("data-original-title",e.attr("title")||"").removeAttr("title")},hasContent:function(){return this.getTitle()},getPosition:function(t){return e.extend({},t?{top:0,left:0}:this.$element.offset(),{width:this.$element[0].offsetWidth,height:this.$element[0].offsetHeight})},getTitle:function(){var e,t=this.$element,n=this.options;return e=t.attr("data-original-title")||(typeof n.title=="function"?n.title.call(t[0]):n.title),e},tip:function(){return this.$tip=this.$tip||e(this.options.template)},validate:function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},enable:function(){this.enabled=!0},disable:function(){this.enabled=!1},toggleEnabled:function(){this.enabled=!this.enabled},toggle:function(t){var n=e(t.currentTarget)[this.type](this._options).data(this.type);n[n.tip().hasClass("in")?"hide":"show"]()},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}},e.fn.tooltip=function(n){return this.each(function(){var r=e(this),i=r.data("tooltip"),s=typeof n=="object"&&n;i||r.data("tooltip",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.tooltip.Constructor=t,e.fn.tooltip.defaults={animation:!0,placement:"top",selector:!1,template:'
    ',trigger:"hover",title:"",delay:0,html:!1}}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("popover",e,t)};t.prototype=e.extend({},e.fn.tooltip.Constructor.prototype,{constructor:t,setContent:function(){var e=this.tip(),t=this.getTitle(),n=this.getContent();e.find(".popover-title")[this.options.html?"html":"text"](t),e.find(".popover-content > *")[this.options.html?"html":"text"](n),e.removeClass("fade top bottom left right in")},hasContent:function(){return this.getTitle()||this.getContent()},getContent:function(){var e,t=this.$element,n=this.options;return e=t.attr("data-content")||(typeof n.content=="function"?n.content.call(t[0]):n.content),e},tip:function(){return this.$tip||(this.$tip=e(this.options.template)),this.$tip},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}}),e.fn.popover=function(n){return this.each(function(){var r=e(this),i=r.data("popover"),s=typeof n=="object"&&n;i||r.data("popover",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.popover.Constructor=t,e.fn.popover.defaults=e.extend({},e.fn.tooltip.defaults,{placement:"right",trigger:"click",content:"",template:'

    '})}(window.jQuery),!function(e){"use strict";function t(t,n){var r=e.proxy(this.process,this),i=e(t).is("body")?e(window):e(t),s;this.options=e.extend({},e.fn.scrollspy.defaults,n),this.$scrollElement=i.on("scroll.scroll-spy.data-api",r),this.selector=(this.options.target||(s=e(t).attr("href"))&&s.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.$body=e("body"),this.refresh(),this.process()}t.prototype={constructor:t,refresh:function(){var t=this,n;this.offsets=e([]),this.targets=e([]),n=this.$body.find(this.selector).map(function(){var t=e(this),n=t.data("target")||t.attr("href"),r=/^#\w/.test(n)&&e(n);return r&&r.length&&[[r.position().top,n]]||null}).sort(function(e,t){return e[0]-t[0]}).each(function(){t.offsets.push(this[0]),t.targets.push(this[1])})},process:function(){var e=this.$scrollElement.scrollTop()+this.options.offset,t=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,n=t-this.$scrollElement.height(),r=this.offsets,i=this.targets,s=this.activeTarget,o;if(e>=n)return s!=(o=i.last()[0])&&this.activate(o);for(o=r.length;o--;)s!=i[o]&&e>=r[o]&&(!r[o+1]||e<=r[o+1])&&this.activate(i[o])},activate:function(t){var n,r;this.activeTarget=t,e(this.selector).parent(".active").removeClass("active"),r=this.selector+'[data-target="'+t+'"],'+this.selector+'[href="'+t+'"]',n=e(r).parent("li").addClass("active"),n.parent(".dropdown-menu").length&&(n=n.closest("li.dropdown").addClass("active")),n.trigger("activate")}},e.fn.scrollspy=function(n){return this.each(function(){var r=e(this),i=r.data("scrollspy"),s=typeof n=="object"&&n;i||r.data("scrollspy",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.scrollspy.Constructor=t,e.fn.scrollspy.defaults={offset:10},e(window).on("load",function(){e('[data-spy="scroll"]').each(function(){var t=e(this);t.scrollspy(t.data())})})}(window.jQuery),!function(e){"use strict";var t=function(t){this.element=e(t)};t.prototype={constructor:t,show:function(){var t=this.element,n=t.closest("ul:not(.dropdown-menu)"),r=t.attr("data-target"),i,s,o;r||(r=t.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,""));if(t.parent("li").hasClass("active"))return;i=n.find(".active:last a")[0],o=e.Event("show",{relatedTarget:i}),t.trigger(o);if(o.isDefaultPrevented())return;s=e(r),this.activate(t.parent("li"),n),this.activate(s,s.parent(),function(){t.trigger({type:"shown",relatedTarget:i})})},activate:function(t,n,r){function o(){i.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),t.addClass("active"),s?(t[0].offsetWidth,t.addClass("in")):t.removeClass("fade"),t.parent(".dropdown-menu")&&t.closest("li.dropdown").addClass("active"),r&&r()}var i=n.find("> .active"),s=r&&e.support.transition&&i.hasClass("fade");s?i.one(e.support.transition.end,o):o(),i.removeClass("in")}},e.fn.tab=function(n){return this.each(function(){var r=e(this),i=r.data("tab");i||r.data("tab",i=new t(this)),typeof n=="string"&&i[n]()})},e.fn.tab.Constructor=t,e(document).on("click.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(t){t.preventDefault(),e(this).tab("show")})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.typeahead.defaults,n),this.matcher=this.options.matcher||this.matcher,this.sorter=this.options.sorter||this.sorter,this.highlighter=this.options.highlighter||this.highlighter,this.updater=this.options.updater||this.updater,this.$menu=e(this.options.menu).appendTo("body"),this.source=this.options.source,this.shown=!1,this.listen()};t.prototype={constructor:t,select:function(){var e=this.$menu.find(".active").attr("data-value");return this.$element.val(this.updater(e)).change(),this.hide()},updater:function(e){return e},show:function(){var t=e.extend({},this.$element.offset(),{height:this.$element[0].offsetHeight});return this.$menu.css({top:t.top+t.height,left:t.left}),this.$menu.show(),this.shown=!0,this},hide:function(){return this.$menu.hide(),this.shown=!1,this},lookup:function(t){var n;return this.query=this.$element.val(),!this.query||this.query.length"+t+""})},render:function(t){var n=this;return t=e(t).map(function(t,r){return t=e(n.options.item).attr("data-value",r),t.find("a").html(n.highlighter(r)),t[0]}),t.first().addClass("active"),this.$menu.html(t),this},next:function(t){var n=this.$menu.find(".active").removeClass("active"),r=n.next();r.length||(r=e(this.$menu.find("li")[0])),r.addClass("active")},prev:function(e){var t=this.$menu.find(".active").removeClass("active"),n=t.prev();n.length||(n=this.$menu.find("li").last()),n.addClass("active")},listen:function(){this.$element.on("blur",e.proxy(this.blur,this)).on("keypress",e.proxy(this.keypress,this)).on("keyup",e.proxy(this.keyup,this)),this.eventSupported("keydown")&&this.$element.on("keydown",e.proxy(this.keydown,this)),this.$menu.on("click",e.proxy(this.click,this)).on("mouseenter","li",e.proxy(this.mouseenter,this))},eventSupported:function(e){var t=e in this.$element;return t||(this.$element.setAttribute(e,"return;"),t=typeof this.$element[e]=="function"),t},move:function(e){if(!this.shown)return;switch(e.keyCode){case 9:case 13:case 27:e.preventDefault();break;case 38:e.preventDefault(),this.prev();break;case 40:e.preventDefault(),this.next()}e.stopPropagation()},keydown:function(t){this.suppressKeyPressRepeat=!~e.inArray(t.keyCode,[40,38,9,13,27]),this.move(t)},keypress:function(e){if(this.suppressKeyPressRepeat)return;this.move(e)},keyup:function(e){switch(e.keyCode){case 40:case 38:case 16:case 17:case 18:break;case 9:case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide();break;default:this.lookup()}e.stopPropagation(),e.preventDefault()},blur:function(e){var t=this;setTimeout(function(){t.hide()},150)},click:function(e){e.stopPropagation(),e.preventDefault(),this.select()},mouseenter:function(t){this.$menu.find(".active").removeClass("active"),e(t.currentTarget).addClass("active")}},e.fn.typeahead=function(n){return this.each(function(){var r=e(this),i=r.data("typeahead"),s=typeof n=="object"&&n;i||r.data("typeahead",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.typeahead.defaults={source:[],items:8,menu:'',item:'
  • ',minLength:1},e.fn.typeahead.Constructor=t,e(document).on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(t){var n=e(this);if(n.data("typeahead"))return;t.preventDefault(),n.typeahead(n.data())})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.options=e.extend({},e.fn.affix.defaults,n),this.$window=e(window).on("scroll.affix.data-api",e.proxy(this.checkPosition,this)).on("click.affix.data-api",e.proxy(function(){setTimeout(e.proxy(this.checkPosition,this),1)},this)),this.$element=e(t),this.checkPosition()};t.prototype.checkPosition=function(){if(!this.$element.is(":visible"))return;var t=e(document).height(),n=this.$window.scrollTop(),r=this.$element.offset(),i=this.options.offset,s=i.bottom,o=i.top,u="affix affix-top affix-bottom",a;typeof i!="object"&&(s=o=i),typeof o=="function"&&(o=i.top()),typeof s=="function"&&(s=i.bottom()),a=this.unpin!=null&&n+this.unpin<=r.top?!1:s!=null&&r.top+this.$element.height()>=t-s?"bottom":o!=null&&n<=o?"top":!1;if(this.affixed===a)return;this.affixed=a,this.unpin=a=="bottom"?r.top-n:null,this.$element.removeClass(u).addClass("affix"+(a?"-"+a:""))},e.fn.affix=function(n){return this.each(function(){var r=e(this),i=r.data("affix"),s=typeof n=="object"&&n;i||r.data("affix",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.affix.Constructor=t,e.fn.affix.defaults={offset:0},e(window).on("load",function(){e('[data-spy="affix"]').each(function(){var t=e(this),n=t.data();n.offset=n.offset||{},n.offsetBottom&&(n.offset.bottom=n.offsetBottom),n.offsetTop&&(n.offset.top=n.offsetTop),t.affix(n)})})}(window.jQuery); -------------------------------------------------------------------------------- /js/vendor/jquery.ba-bbq.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery BBQ: Back Button & Query Library - v1.2.1 - 2/17/2010 3 | * http://benalman.com/projects/jquery-bbq-plugin/ 4 | * 5 | * Copyright (c) 2010 "Cowboy" Ben Alman 6 | * Dual licensed under the MIT and GPL licenses. 7 | * http://benalman.com/about/license/ 8 | */ 9 | (function($,p){var i,m=Array.prototype.slice,r=decodeURIComponent,a=$.param,c,l,v,b=$.bbq=$.bbq||{},q,u,j,e=$.event.special,d="hashchange",A="querystring",D="fragment",y="elemUrlAttr",g="location",k="href",t="src",x=/^.*\?|#.*$/g,w=/^.*\#/,h,C={};function E(F){return typeof F==="string"}function B(G){var F=m.call(arguments,1);return function(){return G.apply(this,F.concat(m.call(arguments)))}}function n(F){return F.replace(/^[^#]*#?(.*)$/,"$1")}function o(F){return F.replace(/(?:^[^?#]*\?([^#]*).*$)?.*/,"$1")}function f(H,M,F,I,G){var O,L,K,N,J;if(I!==i){K=F.match(H?/^([^#]*)\#?(.*)$/:/^([^#?]*)\??([^#]*)(#?.*)/);J=K[3]||"";if(G===2&&E(I)){L=I.replace(H?w:x,"")}else{N=l(K[2]);I=E(I)?l[H?D:A](I):I;L=G===2?I:G===1?$.extend({},I,N):$.extend({},N,I);L=a(L);if(H){L=L.replace(h,r)}}O=K[1]+(H?"#":L||!K[1]?"?":"")+L+J}else{O=M(F!==i?F:p[g][k])}return O}a[A]=B(f,0,o);a[D]=c=B(f,1,n);c.noEscape=function(G){G=G||"";var F=$.map(G.split(""),encodeURIComponent);h=new RegExp(F.join("|"),"g")};c.noEscape(",/");$.deparam=l=function(I,F){var H={},G={"true":!0,"false":!1,"null":null};$.each(I.replace(/\+/g," ").split("&"),function(L,Q){var K=Q.split("="),P=r(K[0]),J,O=H,M=0,R=P.split("]["),N=R.length-1;if(/\[/.test(R[0])&&/\]$/.test(R[N])){R[N]=R[N].replace(/\]$/,"");R=R.shift().split("[").concat(R);N=R.length-1}else{N=0}if(K.length===2){J=r(K[1]);if(F){J=J&&!isNaN(J)?+J:J==="undefined"?i:G[J]!==i?G[J]:J}if(N){for(;M<=N;M++){P=R[M]===""?O.length:R[M];O=O[P]=M').hide().insertAfter("body")[0].contentWindow;q=function(){return a(n.document[c][l])};o=function(u,s){if(u!==s){var t=n.document;t.open().close();t[c].hash="#"+u}};o(a())}}m.start=function(){if(r){return}var t=a();o||p();(function s(){var v=a(),u=q(t);if(v!==t){o(t=v,u);$(i).trigger(d)}else{if(u!==t){i[c][l]=i[c][l].replace(/#.*/,"")+"#"+u}}r=setTimeout(s,$[d+"Delay"])})()};m.stop=function(){if(!n){r&&clearTimeout(r);r=0}};return m})()})(jQuery,this); -------------------------------------------------------------------------------- /js/vendor/jquery.rule-1.0.2-min.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery.Rule - Css Rules manipulation, the jQuery way. 3 | * Copyright (c) 2007-2011 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com 4 | * Dual licensed under MIT and GPL. 5 | * Date: 02/7/2011 6 | * @author Ariel Flesler 7 | * @version 1.0.2 8 | */ 9 | (function(f){var c=f('"].join(""),l.id=h,(m?l:n).innerHTML+=f,n.appendChild(l),m||(n.style.background="",n.style.overflow="hidden",k=g.style.overflow,g.style.overflow="hidden",g.appendChild(n)),i=c(l,a),m?l.parentNode.removeChild(l):(n.parentNode.removeChild(n),g.style.overflow=k),!!i},z=function(b){var c=a.matchMedia||a.msMatchMedia;if(c)return c(b).matches;var d;return y("@media "+b+" { #"+h+" { position: absolute; } }",function(b){d=(a.getComputedStyle?getComputedStyle(b,null):b.currentStyle)["position"]=="absolute"}),d},A=function(){function d(d,e){e=e||b.createElement(a[d]||"div"),d="on"+d;var f=d in e;return f||(e.setAttribute||(e=b.createElement("div")),e.setAttribute&&e.removeAttribute&&(e.setAttribute(d,""),f=F(e[d],"function"),F(e[d],"undefined")||(e[d]=c),e.removeAttribute(d))),e=null,f}var a={select:"input",change:"input",submit:"form",reset:"form",error:"img",load:"img",abort:"img"};return d}(),B={}.hasOwnProperty,C;!F(B,"undefined")&&!F(B.call,"undefined")?C=function(a,b){return B.call(a,b)}:C=function(a,b){return b in a&&F(a.constructor.prototype[b],"undefined")},Function.prototype.bind||(Function.prototype.bind=function(b){var c=this;if(typeof c!="function")throw new TypeError;var d=w.call(arguments,1),e=function(){if(this instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,g=c.apply(f,d.concat(w.call(arguments)));return Object(g)===g?g:f}return c.apply(b,d.concat(w.call(arguments)))};return e}),s.flexbox=function(){return J("flexWrap")},s.canvas=function(){var a=b.createElement("canvas");return!!a.getContext&&!!a.getContext("2d")},s.canvastext=function(){return!!e.canvas&&!!F(b.createElement("canvas").getContext("2d").fillText,"function")},s.webgl=function(){return!!a.WebGLRenderingContext},s.touch=function(){var c;return"ontouchstart"in a||a.DocumentTouch&&b instanceof DocumentTouch?c=!0:y(["@media (",n.join("touch-enabled),("),h,")","{#modernizr{top:9px;position:absolute}}"].join(""),function(a){c=a.offsetTop===9}),c},s.geolocation=function(){return"geolocation"in navigator},s.postmessage=function(){return!!a.postMessage},s.websqldatabase=function(){return!!a.openDatabase},s.indexedDB=function(){return!!J("indexedDB",a)},s.hashchange=function(){return A("hashchange",a)&&(b.documentMode===c||b.documentMode>7)},s.history=function(){return!!a.history&&!!history.pushState},s.draganddrop=function(){var a=b.createElement("div");return"draggable"in a||"ondragstart"in a&&"ondrop"in a},s.websockets=function(){return"WebSocket"in a||"MozWebSocket"in a},s.rgba=function(){return D("background-color:rgba(150,255,150,.5)"),G(j.backgroundColor,"rgba")},s.hsla=function(){return D("background-color:hsla(120,40%,100%,.5)"),G(j.backgroundColor,"rgba")||G(j.backgroundColor,"hsla")},s.multiplebgs=function(){return D("background:url(https://),url(https://),red url(https://)"),/(url\s*\(.*?){3}/.test(j.background)},s.backgroundsize=function(){return J("backgroundSize")},s.borderimage=function(){return J("borderImage")},s.borderradius=function(){return J("borderRadius")},s.boxshadow=function(){return J("boxShadow")},s.textshadow=function(){return b.createElement("div").style.textShadow===""},s.opacity=function(){return E("opacity:.55"),/^0.55$/.test(j.opacity)},s.cssanimations=function(){return J("animationName")},s.csscolumns=function(){return J("columnCount")},s.cssgradients=function(){var a="background-image:",b="gradient(linear,left top,right bottom,from(#9f9),to(white));",c="linear-gradient(left top,#9f9, white);";return D((a+"-webkit- ".split(" ").join(b+a)+n.join(c+a)).slice(0,-a.length)),G(j.backgroundImage,"gradient")},s.cssreflections=function(){return J("boxReflect")},s.csstransforms=function(){return!!J("transform")},s.csstransforms3d=function(){var a=!!J("perspective");return a&&"webkitPerspective"in g.style&&y("@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}",function(b,c){a=b.offsetLeft===9&&b.offsetHeight===3}),a},s.csstransitions=function(){return J("transition")},s.fontface=function(){var a;return y('@font-face {font-family:"font";src:url("https://")}',function(c,d){var e=b.getElementById("smodernizr"),f=e.sheet||e.styleSheet,g=f?f.cssRules&&f.cssRules[0]?f.cssRules[0].cssText:f.cssText||"":"";a=/src/i.test(g)&&g.indexOf(d.split(" ")[0])===0}),a},s.generatedcontent=function(){var a;return y(["#",h,"{font:0/0 a}#",h,':after{content:"',l,'";visibility:hidden;font:3px/1 a}'].join(""),function(b){a=b.offsetHeight>=3}),a},s.video=function(){var a=b.createElement("video"),c=!1;try{if(c=!!a.canPlayType)c=new Boolean(c),c.ogg=a.canPlayType('video/ogg; codecs="theora"').replace(/^no$/,""),c.h264=a.canPlayType('video/mp4; codecs="avc1.42E01E"').replace(/^no$/,""),c.webm=a.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/,"")}catch(d){}return c},s.audio=function(){var a=b.createElement("audio"),c=!1;try{if(c=!!a.canPlayType)c=new Boolean(c),c.ogg=a.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,""),c.mp3=a.canPlayType("audio/mpeg;").replace(/^no$/,""),c.wav=a.canPlayType('audio/wav; codecs="1"').replace(/^no$/,""),c.m4a=(a.canPlayType("audio/x-m4a;")||a.canPlayType("audio/aac;")).replace(/^no$/,"")}catch(d){}return c},s.localstorage=function(){try{return localStorage.setItem(h,h),localStorage.removeItem(h),!0}catch(a){return!1}},s.sessionstorage=function(){try{return sessionStorage.setItem(h,h),sessionStorage.removeItem(h),!0}catch(a){return!1}},s.webworkers=function(){return!!a.Worker},s.applicationcache=function(){return!!a.applicationCache},s.svg=function(){return!!b.createElementNS&&!!b.createElementNS(r.svg,"svg").createSVGRect},s.inlinesvg=function(){var a=b.createElement("div");return a.innerHTML="",(a.firstChild&&a.firstChild.namespaceURI)==r.svg},s.smil=function(){return!!b.createElementNS&&/SVGAnimate/.test(m.call(b.createElementNS(r.svg,"animate")))},s.svgclippaths=function(){return!!b.createElementNS&&/SVGClipPath/.test(m.call(b.createElementNS(r.svg,"clipPath")))};for(var L in s)C(s,L)&&(x=L.toLowerCase(),e[x]=s[L](),v.push((e[x]?"":"no-")+x));return e.input||K(),e.addTest=function(a,b){if(typeof a=="object")for(var d in a)C(a,d)&&e.addTest(d,a[d]);else{a=a.toLowerCase();if(e[a]!==c)return e;b=typeof b=="function"?b():b,typeof f!="undefined"&&f&&(g.className+=" "+(b?"":"no-")+a),e[a]=b}return e},D(""),i=k=null,function(a,b){function k(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function l(){var a=r.elements;return typeof a=="string"?a.split(" "):a}function m(a){var b=i[a[g]];return b||(b={},h++,a[g]=h,i[h]=b),b}function n(a,c,f){c||(c=b);if(j)return c.createElement(a);f||(f=m(c));var g;return f.cache[a]?g=f.cache[a].cloneNode():e.test(a)?g=(f.cache[a]=f.createElem(a)).cloneNode():g=f.createElem(a),g.canHaveChildren&&!d.test(a)?f.frag.appendChild(g):g}function o(a,c){a||(a=b);if(j)return a.createDocumentFragment();c=c||m(a);var d=c.frag.cloneNode(),e=0,f=l(),g=f.length;for(;e",f="hidden"in a,j=a.childNodes.length==1||function(){b.createElement("a");var a=b.createDocumentFragment();return typeof a.cloneNode=="undefined"||typeof a.createDocumentFragment=="undefined"||typeof a.createElement=="undefined"}()}catch(c){f=!0,j=!0}})();var r={elements:c.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video",shivCSS:c.shivCSS!==!1,supportsUnknownElements:j,shivMethods:c.shivMethods!==!1,type:"default",shivDocument:q,createElement:n,createDocumentFragment:o};a.html5=r,q(b)}(this,b),e._version=d,e._prefixes=n,e._domPrefixes=q,e._cssomPrefixes=p,e.mq=z,e.hasEvent=A,e.testProp=function(a){return H([a])},e.testAllProps=J,e.testStyles=y,e.prefixed=function(a,b,c){return b?J(a,b,c):J(a,"pfx")},g.className=g.className.replace(/(^|\s)no-js(\s|$)/,"$1$2")+(f?" js "+v.join(" "):""),e}(this,this.document),function(a,b,c){function d(a){return"[object Function]"==o.call(a)}function e(a){return"string"==typeof a}function f(){}function g(a){return!a||"loaded"==a||"complete"==a||"uninitialized"==a}function h(){var a=p.shift();q=1,a?a.t?m(function(){("c"==a.t?B.injectCss:B.injectJs)(a.s,0,a.a,a.x,a.e,1)},0):(a(),h()):q=0}function i(a,c,d,e,f,i,j){function k(b){if(!o&&g(l.readyState)&&(u.r=o=1,!q&&h(),l.onload=l.onreadystatechange=null,b)){"img"!=a&&m(function(){t.removeChild(l)},50);for(var d in y[c])y[c].hasOwnProperty(d)&&y[c][d].onload()}}var j=j||B.errorTimeout,l=b.createElement(a),o=0,r=0,u={t:d,s:c,e:f,a:i,x:j};1===y[c]&&(r=1,y[c]=[]),"object"==a?l.data=c:(l.src=c,l.type=a),l.width=l.height="0",l.onerror=l.onload=l.onreadystatechange=function(){k.call(this,r)},p.splice(e,0,u),"img"!=a&&(r||2===y[c]?(t.insertBefore(l,s?null:n),m(k,j)):y[c].push(l))}function j(a,b,c,d,f){return q=0,b=b||"j",e(a)?i("c"==b?v:u,a,b,this.i++,c,d,f):(p.splice(this.i++,0,a),1==p.length&&h()),this}function k(){var a=B;return a.loader={load:j,i:0},a}var l=b.documentElement,m=a.setTimeout,n=b.getElementsByTagName("script")[0],o={}.toString,p=[],q=0,r="MozAppearance"in l.style,s=r&&!!b.createRange().compareNode,t=s?l:n.parentNode,l=a.opera&&"[object Opera]"==o.call(a.opera),l=!!b.attachEvent&&!l,u=r?"object":l?"script":"img",v=l?"script":u,w=Array.isArray||function(a){return"[object Array]"==o.call(a)},x=[],y={},z={timeout:function(a,b){return b.length&&(a.timeout=b[0]),a}},A,B;B=function(a){function b(a){var a=a.split("!"),b=x.length,c=a.pop(),d=a.length,c={url:c,origUrl:c,prefixes:a},e,f,g;for(f=0;f #mq-test-1 { width: 42px; }';a.insertBefore(d,b);c=g.offsetWidth==42;a.removeChild(d);return{matches:c,media:h}}})(document); 9 | 10 | /*! Respond.js v1.1.0: min/max-width media query polyfill. (c) Scott Jehl. MIT/GPLv2 Lic. j.mp/respondjs */ 11 | (function(e){e.respond={};respond.update=function(){};respond.mediaQueriesSupported=e.matchMedia&&e.matchMedia("only all").matches;if(respond.mediaQueriesSupported){return}var w=e.document,s=w.documentElement,i=[],k=[],q=[],o={},h=30,f=w.getElementsByTagName("head")[0]||s,g=w.getElementsByTagName("base")[0],b=f.getElementsByTagName("link"),d=[],a=function(){var D=b,y=D.length,B=0,A,z,C,x;for(;B-1,minw:F.match(/\(min\-width:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:F.match(/\(max\-width:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}}j()},l,r,v=function(){var z,A=w.createElement("div"),x=w.body,y=false;A.style.cssText="position:absolute;font-size:1em;width:1em";if(!x){x=y=w.createElement("body");x.style.background="none"}x.appendChild(A);s.insertBefore(x,s.firstChild);z=A.offsetWidth;if(y){s.removeChild(x)}else{x.removeChild(A)}z=p=parseFloat(z);return z},p,j=function(I){var x="clientWidth",B=s[x],H=w.compatMode==="CSS1Compat"&&B||w.body[x]||B,D={},G=b[b.length-1],z=(new Date()).getTime();if(I&&l&&z-l-1?(p||v()):1)}if(!!J){J=parseFloat(J)*(J.indexOf(y)>-1?(p||v()):1)}if(!K.hasquery||(!A||!L)&&(A||H>=C)&&(L||H<=J)){if(!D[K.media]){D[K.media]=[]}D[K.media].push(k[K.rules])}}for(var E in q){if(q[E]&&q[E].parentNode===f){f.removeChild(q[E])}}for(var E in D){var M=w.createElement("style"),F=D[E].join("\n");M.type="text/css";M.media=E;f.insertBefore(M,G.nextSibling);if(M.styleSheet){M.styleSheet.cssText=F}else{M.appendChild(w.createTextNode(F))}q.push(M)}},n=function(x,z){var y=c();if(!y){return}y.open("GET",x,true);y.onreadystatechange=function(){if(y.readyState!=4||y.status!=200&&y.status!=304){return}z(y.responseText)};if(y.readyState==4){return}y.send(null)},c=(function(){var x=false;try{x=new XMLHttpRequest()}catch(y){x=new ActiveXObject("Microsoft.XMLHTTP")}return function(){return x}})();a();respond.update=a;function t(){j(true)}if(e.addEventListener){e.addEventListener("resize",t,false)}else{if(e.attachEvent){e.attachEvent("onresize",t)}}})(this); -------------------------------------------------------------------------------- /js/vendor/mustache.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * mustache.js - Logic-less {{mustache}} templates with JavaScript 3 | * http://github.com/janl/mustache.js 4 | */ 5 | 6 | /*global define: false*/ 7 | 8 | var Mustache; 9 | 10 | (function (exports) { 11 | if (typeof module !== "undefined" && module.exports) { 12 | module.exports = exports; // CommonJS 13 | } else if (typeof define === "function") { 14 | define(exports); // AMD 15 | } else { 16 | Mustache = exports; //