Merge pull request #219 from rashidkpc/query_service

Refactor of queries, filters, dashboard service
This commit is contained in:
Rashid Khan 2013-07-15 16:17:26 -07:00
commit fdb3492825
79 changed files with 1747 additions and 5003 deletions

View File

@ -5,8 +5,16 @@ Kibana 3 is completely new version of Kibana written entirely in HTML and Javasc
the Kibana 2 repository at [https://github.com/rashidkpc/Kibana](https://github.com/rashidkpc/Kibana)
### Important!
The index pattern format has changed in Kibana 3 milestone 2. Please update your index pattern in the
timepicker panel for any dashboards you've built. The default has been updated.
The dashboard storage format has changed in Kibana 3 milestone 3. Existing dashboards are unfortunately not backward compatible. However there's some great new features:
* Every panel support multi-query
* Customizable query colors and labels
* Queries, label and colors are synced across panels at all times
* New filtering functionality
* Filters can be toggled and removed
* Drill down won't overwrite your queries, labels or colors
* Confusing group functionality has been removed
* Index configuration has been moved from the timepicker, to the main dashboard editor
* The stringquery panel has been replaced with a more polished 'query' panel
More information about Kibana 3 can be found at [http://three.kibana.org](http://three.kibana.org)

9
common/css/bootstrap.light.min.css vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,858 +0,0 @@
.clearfix{*zoom:1;}.clearfix:before,.clearfix:after{display:table;content:"";line-height:0;}
.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;}
article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block;}
audio,canvas,video{display:inline-block;*display:inline;*zoom:1;}
audio:not([controls]){display:none;}
html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}
a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;}
a:hover,a:active{outline:0;}
sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline;}
sup{top:-0.5em;}
sub{bottom:-0.25em;}
img{max-width:100%;width:auto\9;height:auto;vertical-align:middle;border:0;-ms-interpolation-mode:bicubic;}
#map_canvas img,.google-maps img{max-width:none;}
button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle;}
button,input{*overflow:visible;line-height:normal;}
button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0;}
button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer;}
label,select,button,input[type="button"],input[type="reset"],input[type="submit"],input[type="radio"],input[type="checkbox"]{cursor:pointer;}
input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield;}
input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none;}
textarea{overflow:auto;vertical-align:top;}
@media print{*{text-shadow:none !important;color:#000 !important;background:transparent !important;box-shadow:none !important;} a,a:visited{text-decoration:underline;} a[href]:after{content:" (" attr(href) ")";} abbr[title]:after{content:" (" attr(title) ")";} .ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:"";} pre,blockquote{border:1px solid #999;page-break-inside:avoid;} thead{display:table-header-group;} tr,img{page-break-inside:avoid;} img{max-width:100% !important;} @page {margin:0.5cm;}p,h2,h3{orphans:3;widows:3;} h2,h3{page-break-after:avoid;}}body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:20px;color:#333333;background-color:#ffffff;}
a{color:#e66700;text-decoration:none;}
a:hover,a:focus{color:#9a4500;text-decoration:underline;}
.img-rounded{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}
.img-polaroid{padding:4px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.2);-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.1);-moz-box-shadow:0 1px 3px rgba(0, 0, 0, 0.1);box-shadow:0 1px 3px rgba(0, 0, 0, 0.1);}
.img-circle{-webkit-border-radius:500px;-moz-border-radius:500px;border-radius:500px;}
.row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:"";line-height:0;}
.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:940px;}
.span12{width:940px;}
.span11{width:860px;}
.span10{width:780px;}
.span9{width:700px;}
.span8{width:620px;}
.span7{width:540px;}
.span6{width:460px;}
.span5{width:380px;}
.span4{width:300px;}
.span3{width:220px;}
.span2{width:140px;}
.span1{width:60px;}
.offset12{margin-left:980px;}
.offset11{margin-left:900px;}
.offset10{margin-left:820px;}
.offset9{margin-left:740px;}
.offset8{margin-left:660px;}
.offset7{margin-left:580px;}
.offset6{margin-left:500px;}
.offset5{margin-left:420px;}
.offset4{margin-left:340px;}
.offset3{margin-left:260px;}
.offset2{margin-left:180px;}
.offset1{margin-left:100px;}
.row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";line-height:0;}
.row-fluid:after{clear:both;}
.row-fluid [class*="span"]{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;float:left;margin-left:2.127659574468085%;*margin-left:2.074468085106383%;}
.row-fluid [class*="span"]:first-child{margin-left:0;}
.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.127659574468085%;}
.row-fluid .span12{width:100%;*width:99.94680851063829%;}
.row-fluid .span11{width:91.48936170212765%;*width:91.43617021276594%;}
.row-fluid .span10{width:82.97872340425532%;*width:82.92553191489361%;}
.row-fluid .span9{width:74.46808510638297%;*width:74.41489361702126%;}
.row-fluid .span8{width:65.95744680851064%;*width:65.90425531914893%;}
.row-fluid .span7{width:57.44680851063829%;*width:57.39361702127659%;}
.row-fluid .span6{width:48.93617021276595%;*width:48.88297872340425%;}
.row-fluid .span5{width:40.42553191489362%;*width:40.37234042553192%;}
.row-fluid .span4{width:31.914893617021278%;*width:31.861702127659576%;}
.row-fluid .span3{width:23.404255319148934%;*width:23.351063829787233%;}
.row-fluid .span2{width:14.893617021276595%;*width:14.840425531914894%;}
.row-fluid .span1{width:6.382978723404255%;*width:6.329787234042553%;}
.row-fluid .offset12{margin-left:104.25531914893617%;*margin-left:104.14893617021275%;}
.row-fluid .offset12:first-child{margin-left:102.12765957446808%;*margin-left:102.02127659574467%;}
.row-fluid .offset11{margin-left:95.74468085106382%;*margin-left:95.6382978723404%;}
.row-fluid .offset11:first-child{margin-left:93.61702127659574%;*margin-left:93.51063829787232%;}
.row-fluid .offset10{margin-left:87.23404255319149%;*margin-left:87.12765957446807%;}
.row-fluid .offset10:first-child{margin-left:85.1063829787234%;*margin-left:84.99999999999999%;}
.row-fluid .offset9{margin-left:78.72340425531914%;*margin-left:78.61702127659572%;}
.row-fluid .offset9:first-child{margin-left:76.59574468085106%;*margin-left:76.48936170212764%;}
.row-fluid .offset8{margin-left:70.2127659574468%;*margin-left:70.10638297872339%;}
.row-fluid .offset8:first-child{margin-left:68.08510638297872%;*margin-left:67.9787234042553%;}
.row-fluid .offset7{margin-left:61.70212765957446%;*margin-left:61.59574468085106%;}
.row-fluid .offset7:first-child{margin-left:59.574468085106375%;*margin-left:59.46808510638297%;}
.row-fluid .offset6{margin-left:53.191489361702125%;*margin-left:53.085106382978715%;}
.row-fluid .offset6:first-child{margin-left:51.063829787234035%;*margin-left:50.95744680851063%;}
.row-fluid .offset5{margin-left:44.68085106382979%;*margin-left:44.57446808510638%;}
.row-fluid .offset5:first-child{margin-left:42.5531914893617%;*margin-left:42.4468085106383%;}
.row-fluid .offset4{margin-left:36.170212765957444%;*margin-left:36.06382978723405%;}
.row-fluid .offset4:first-child{margin-left:34.04255319148936%;*margin-left:33.93617021276596%;}
.row-fluid .offset3{margin-left:27.659574468085104%;*margin-left:27.5531914893617%;}
.row-fluid .offset3:first-child{margin-left:25.53191489361702%;*margin-left:25.425531914893618%;}
.row-fluid .offset2{margin-left:19.148936170212764%;*margin-left:19.04255319148936%;}
.row-fluid .offset2:first-child{margin-left:17.02127659574468%;*margin-left:16.914893617021278%;}
.row-fluid .offset1{margin-left:10.638297872340425%;*margin-left:10.53191489361702%;}
.row-fluid .offset1:first-child{margin-left:8.51063829787234%;*margin-left:8.404255319148938%;}
[class*="span"].hide,.row-fluid [class*="span"].hide{display:none;}
[class*="span"].pull-right,.row-fluid [class*="span"].pull-right{float:right;}
.container{margin-right:auto;margin-left:auto;*zoom:1;}.container:before,.container:after{display:table;content:"";line-height:0;}
.container:after{clear:both;}
.container-fluid{padding-right:20px;padding-left:20px;*zoom:1;}.container-fluid:before,.container-fluid:after{display:table;content:"";line-height:0;}
.container-fluid:after{clear:both;}
p{margin:0 0 10px;}
.lead{margin-bottom:20px;font-size:21px;font-weight:200;line-height:30px;}
small{font-size:85%;}
strong{font-weight:bold;}
em{font-style:italic;}
cite{font-style:normal;}
.muted{color:#4d4d4d;}
a.muted:hover,a.muted:focus{color:#333333;}
.text-warning{color:#c09853;}
a.text-warning:hover,a.text-warning:focus{color:#a47e3c;}
.text-error{color:#b94a48;}
a.text-error:hover,a.text-error:focus{color:#953b39;}
.text-info{color:#3a87ad;}
a.text-info:hover,a.text-info:focus{color:#2d6987;}
.text-success{color:#468847;}
a.text-success:hover,a.text-success:focus{color:#356635;}
.text-left{text-align:left;}
.text-right{text-align:right;}
.text-center{text-align:center;}
h1,h2,h3,h4,h5,h6{margin:10px 0;font-family:inherit;font-weight:bold;line-height:20px;color:inherit;text-rendering:optimizelegibility;}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;line-height:1;color:#4d4d4d;}
h1,h2,h3{line-height:40px;}
h1{font-size:38.5px;}
h2{font-size:31.5px;}
h3{font-size:24.5px;}
h4{font-size:17.5px;}
h5{font-size:14px;}
h6{font-size:11.9px;}
h1 small{font-size:24.5px;}
h2 small{font-size:17.5px;}
h3 small{font-size:14px;}
h4 small{font-size:14px;}
.page-header{padding-bottom:9px;margin:20px 0 30px;border-bottom:1px solid #aaaaaa;}
ul,ol{padding:0;margin:0 0 10px 25px;}
ul ul,ul ol,ol ol,ol ul{margin-bottom:0;}
li{line-height:20px;}
ul.unstyled,ol.unstyled{margin-left:0;list-style:none;}
ul.inline,ol.inline{margin-left:0;list-style:none;}ul.inline>li,ol.inline>li{display:inline-block;*display:inline;*zoom:1;padding-left:5px;padding-right:5px;}
dl{margin-bottom:20px;}
dt,dd{line-height:20px;}
dt{font-weight:bold;}
dd{margin-left:10px;}
.dl-horizontal{*zoom:1;}.dl-horizontal:before,.dl-horizontal:after{display:table;content:"";line-height:0;}
.dl-horizontal:after{clear:both;}
.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.dl-horizontal dd{margin-left:180px;}
hr{margin:20px 0;border:0;border-top:1px solid #aaaaaa;border-bottom:1px solid #ffffff;}
abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #4d4d4d;}
abbr.initialism{font-size:90%;text-transform:uppercase;}
blockquote{padding:0 0 0 15px;margin:0 0 20px;border-left:5px solid #aaaaaa;}blockquote p{margin-bottom:0;font-size:17.5px;font-weight:300;line-height:1.25;}
blockquote small{display:block;line-height:20px;color:#4d4d4d;}blockquote small:before{content:'\2014 \00A0';}
blockquote.pull-right{float:right;padding-right:15px;padding-left:0;border-right:5px solid #aaaaaa;border-left:0;}blockquote.pull-right p,blockquote.pull-right small{text-align:right;}
blockquote.pull-right small:before{content:'';}
blockquote.pull-right small:after{content:'\00A0 \2014';}
q:before,q:after,blockquote:before,blockquote:after{content:"";}
address{display:block;margin-bottom:20px;font-style:normal;line-height:20px;}
code,pre{padding:0 3px 2px;font-family:Monaco,Menlo,Consolas,"Courier New",monospace;font-size:12px;color:#333333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
code{padding:2px 4px;color:#d14;background-color:#f7f7f9;border:1px solid #e1e1e8;white-space:nowrap;}
pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:20px;word-break:break-all;word-wrap:break-word;white-space:pre;white-space:pre-wrap;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}pre.prettyprint{margin-bottom:20px;}
pre code{padding:0;color:inherit;white-space:pre;white-space:pre-wrap;background-color:transparent;border:0;}
.pre-scrollable{max-height:340px;overflow-y:scroll;}
form{margin:0 0 20px;}
fieldset{padding:0;margin:0;border:0;}
legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:40px;color:#333333;border:0;border-bottom:1px solid #e5e5e5;}legend small{font-size:15px;color:#4d4d4d;}
label,input,button,select,textarea{font-size:14px;font-weight:normal;line-height:20px;}
input,button,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;}
label{display:block;margin-bottom:5px;}
select,textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{display:inline-block;height:20px;padding:4px 6px;margin-bottom:10px;font-size:14px;line-height:20px;color:#333333;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;vertical-align:middle;}
input,textarea,.uneditable-input{width:206px;}
textarea{height:auto;}
textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{background-color:#ffffff;border:1px solid #cccccc;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-webkit-transition:border linear .2s, box-shadow linear .2s;-moz-transition:border linear .2s, box-shadow linear .2s;-o-transition:border linear .2s, box-shadow linear .2s;transition:border linear .2s, box-shadow linear .2s;}textarea:focus,input[type="text"]:focus,input[type="password"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus,.uneditable-input:focus{border-color:rgba(82, 168, 236, 0.8);outline:0;outline:thin dotted \9;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6);}
input[type="radio"],input[type="checkbox"]{margin:4px 0 0;*margin-top:0;margin-top:1px \9;line-height:normal;}
input[type="file"],input[type="image"],input[type="submit"],input[type="reset"],input[type="button"],input[type="radio"],input[type="checkbox"]{width:auto;}
select,input[type="file"]{height:30px;*margin-top:4px;line-height:30px;}
select{width:220px;border:1px solid #cccccc;background-color:#ffffff;}
select[multiple],select[size]{height:auto;}
select:focus,input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;}
.uneditable-input,.uneditable-textarea{color:#4d4d4d;background-color:#fcfcfc;border-color:#cccccc;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);cursor:not-allowed;}
.uneditable-input{overflow:hidden;white-space:nowrap;}
.uneditable-textarea{width:auto;height:auto;}
input:-moz-placeholder,textarea:-moz-placeholder{color:#4d4d4d;}
input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#4d4d4d;}
input:-webkit-input-placeholder{color:#4d4d4d;}
textarea:-webkit-input-placeholder{color:#4d4d4d;}
.radio,.checkbox{min-height:20px;padding-left:20px;}
.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-left:-20px;}
.controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px;}
.radio.inline,.checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle;}
.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px;}
.input-mini{width:60px;}
.input-small{width:90px;}
.input-medium{width:150px;}
.input-large{width:210px;}
.input-xlarge{width:270px;}
.input-xxlarge{width:530px;}
input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"]{float:none;margin-left:0;}
.input-append input[class*="span"],.input-append .uneditable-input[class*="span"],.input-prepend input[class*="span"],.input-prepend .uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"],.row-fluid .input-prepend [class*="span"],.row-fluid .input-append [class*="span"]{display:inline-block;}
input,textarea,.uneditable-input{margin-left:0;}
.controls-row [class*="span"]+[class*="span"]{margin-left:20px;}
input.span12,textarea.span12,.uneditable-input.span12{width:926px;}
input.span11,textarea.span11,.uneditable-input.span11{width:846px;}
input.span10,textarea.span10,.uneditable-input.span10{width:766px;}
input.span9,textarea.span9,.uneditable-input.span9{width:686px;}
input.span8,textarea.span8,.uneditable-input.span8{width:606px;}
input.span7,textarea.span7,.uneditable-input.span7{width:526px;}
input.span6,textarea.span6,.uneditable-input.span6{width:446px;}
input.span5,textarea.span5,.uneditable-input.span5{width:366px;}
input.span4,textarea.span4,.uneditable-input.span4{width:286px;}
input.span3,textarea.span3,.uneditable-input.span3{width:206px;}
input.span2,textarea.span2,.uneditable-input.span2{width:126px;}
input.span1,textarea.span1,.uneditable-input.span1{width:46px;}
.controls-row{*zoom:1;}.controls-row:before,.controls-row:after{display:table;content:"";line-height:0;}
.controls-row:after{clear:both;}
.controls-row [class*="span"],.row-fluid .controls-row [class*="span"]{float:left;}
.controls-row .checkbox[class*="span"],.controls-row .radio[class*="span"]{padding-top:5px;}
input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#eeeeee;}
input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"][readonly],input[type="checkbox"][readonly]{background-color:transparent;}
.control-group.warning .control-label,.control-group.warning .help-block,.control-group.warning .help-inline{color:#c09853;}
.control-group.warning .checkbox,.control-group.warning .radio,.control-group.warning input,.control-group.warning select,.control-group.warning textarea{color:#c09853;}
.control-group.warning input,.control-group.warning select,.control-group.warning textarea{border-color:#c09853;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);}.control-group.warning input:focus,.control-group.warning select:focus,.control-group.warning textarea:focus{border-color:#a47e3c;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 6px #dbc59e;-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 6px #dbc59e;box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 6px #dbc59e;}
.control-group.warning .input-prepend .add-on,.control-group.warning .input-append .add-on{color:#c09853;background-color:#fcf8e3;border-color:#c09853;}
.control-group.error .control-label,.control-group.error .help-block,.control-group.error .help-inline{color:#b94a48;}
.control-group.error .checkbox,.control-group.error .radio,.control-group.error input,.control-group.error select,.control-group.error textarea{color:#b94a48;}
.control-group.error input,.control-group.error select,.control-group.error textarea{border-color:#b94a48;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);}.control-group.error input:focus,.control-group.error select:focus,.control-group.error textarea:focus{border-color:#953b39;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 6px #d59392;-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 6px #d59392;box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 6px #d59392;}
.control-group.error .input-prepend .add-on,.control-group.error .input-append .add-on{color:#b94a48;background-color:#f2dede;border-color:#b94a48;}
.control-group.success .control-label,.control-group.success .help-block,.control-group.success .help-inline{color:#468847;}
.control-group.success .checkbox,.control-group.success .radio,.control-group.success input,.control-group.success select,.control-group.success textarea{color:#468847;}
.control-group.success input,.control-group.success select,.control-group.success textarea{border-color:#468847;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);}.control-group.success input:focus,.control-group.success select:focus,.control-group.success textarea:focus{border-color:#356635;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 6px #7aba7b;-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 6px #7aba7b;box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 6px #7aba7b;}
.control-group.success .input-prepend .add-on,.control-group.success .input-append .add-on{color:#468847;background-color:#dff0d8;border-color:#468847;}
.control-group.info .control-label,.control-group.info .help-block,.control-group.info .help-inline{color:#3a87ad;}
.control-group.info .checkbox,.control-group.info .radio,.control-group.info input,.control-group.info select,.control-group.info textarea{color:#3a87ad;}
.control-group.info input,.control-group.info select,.control-group.info textarea{border-color:#3a87ad;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);}.control-group.info input:focus,.control-group.info select:focus,.control-group.info textarea:focus{border-color:#2d6987;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 6px #7ab5d3;-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 6px #7ab5d3;box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 6px #7ab5d3;}
.control-group.info .input-prepend .add-on,.control-group.info .input-append .add-on{color:#3a87ad;background-color:#d9edf7;border-color:#3a87ad;}
input:focus:invalid,textarea:focus:invalid,select:focus:invalid{color:#b94a48;border-color:#ee5f5b;}input:focus:invalid:focus,textarea:focus:invalid:focus,select:focus:invalid:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7;}
.form-actions{padding:19px 20px 20px;margin-top:20px;margin-bottom:20px;background-color:#f5f5f5;border-top:1px solid #e5e5e5;*zoom:1;}.form-actions:before,.form-actions:after{display:table;content:"";line-height:0;}
.form-actions:after{clear:both;}
.help-block,.help-inline{color:#595959;}
.help-block{display:block;margin-bottom:10px;}
.help-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle;padding-left:5px;}
.input-append,.input-prepend{display:inline-block;margin-bottom:10px;vertical-align:middle;font-size:0;white-space:nowrap;}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input,.input-append .dropdown-menu,.input-prepend .dropdown-menu,.input-append .popover,.input-prepend .popover{font-size:14px;}
.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input{position:relative;margin-bottom:0;*margin-left:0;vertical-align:top;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0;}.input-append input:focus,.input-prepend input:focus,.input-append select:focus,.input-prepend select:focus,.input-append .uneditable-input:focus,.input-prepend .uneditable-input:focus{z-index:2;}
.input-append .add-on,.input-prepend .add-on{display:inline-block;width:auto;height:20px;min-width:16px;padding:4px 5px;font-size:14px;font-weight:normal;line-height:20px;text-align:center;text-shadow:0 1px 0 #ffffff;background-color:#eeeeee;border:1px solid #ccc;}
.input-append .add-on,.input-prepend .add-on,.input-append .btn,.input-prepend .btn,.input-append .btn-group>.dropdown-toggle,.input-prepend .btn-group>.dropdown-toggle{vertical-align:top;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
.input-append .active,.input-prepend .active{background-color:#a9dba9;border-color:#46a546;}
.input-prepend .add-on,.input-prepend .btn{margin-right:-1px;}
.input-prepend .add-on:first-child,.input-prepend .btn:first-child{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px;}
.input-append input,.input-append select,.input-append .uneditable-input{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px;}.input-append input+.btn-group .btn:last-child,.input-append select+.btn-group .btn:last-child,.input-append .uneditable-input+.btn-group .btn:last-child{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0;}
.input-append .add-on,.input-append .btn,.input-append .btn-group{margin-left:-1px;}
.input-append .add-on:last-child,.input-append .btn:last-child,.input-append .btn-group:last-child>.dropdown-toggle{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0;}
.input-prepend.input-append input,.input-prepend.input-append select,.input-prepend.input-append .uneditable-input{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}.input-prepend.input-append input+.btn-group .btn,.input-prepend.input-append select+.btn-group .btn,.input-prepend.input-append .uneditable-input+.btn-group .btn{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0;}
.input-prepend.input-append .add-on:first-child,.input-prepend.input-append .btn:first-child{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px;}
.input-prepend.input-append .add-on:last-child,.input-prepend.input-append .btn:last-child{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0;}
.input-prepend.input-append .btn-group:first-child{margin-left:0;}
input.search-query{padding-right:14px;padding-right:4px \9;padding-left:14px;padding-left:4px \9;margin-bottom:0;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px;}
.form-search .input-append .search-query,.form-search .input-prepend .search-query{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
.form-search .input-append .search-query{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px;}
.form-search .input-append .btn{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0;}
.form-search .input-prepend .search-query{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0;}
.form-search .input-prepend .btn{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px;}
.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input,.form-search .input-prepend,.form-inline .input-prepend,.form-horizontal .input-prepend,.form-search .input-append,.form-inline .input-append,.form-horizontal .input-append{display:inline-block;*display:inline;*zoom:1;margin-bottom:0;vertical-align:middle;}
.form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none;}
.form-search label,.form-inline label,.form-search .btn-group,.form-inline .btn-group{display:inline-block;}
.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{margin-bottom:0;}
.form-search .radio,.form-search .checkbox,.form-inline .radio,.form-inline .checkbox{padding-left:0;margin-bottom:0;vertical-align:middle;}
.form-search .radio input[type="radio"],.form-search .checkbox input[type="checkbox"],.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:left;margin-right:3px;margin-left:0;}
.control-group{margin-bottom:10px;}
legend+.control-group{margin-top:20px;-webkit-margin-top-collapse:separate;}
.form-horizontal .control-group{margin-bottom:20px;*zoom:1;}.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;content:"";line-height:0;}
.form-horizontal .control-group:after{clear:both;}
.form-horizontal .control-label{float:left;width:160px;padding-top:5px;text-align:right;}
.form-horizontal .controls{*display:inline-block;*padding-left:20px;margin-left:180px;*margin-left:0;}.form-horizontal .controls:first-child{*padding-left:180px;}
.form-horizontal .help-block{margin-bottom:0;}
.form-horizontal input+.help-block,.form-horizontal select+.help-block,.form-horizontal textarea+.help-block,.form-horizontal .uneditable-input+.help-block,.form-horizontal .input-prepend+.help-block,.form-horizontal .input-append+.help-block{margin-top:10px;}
.form-horizontal .form-actions{padding-left:180px;}
table{max-width:100%;background-color:transparent;border-collapse:collapse;border-spacing:0;}
.table{width:100%;margin-bottom:20px;}.table th,.table td{padding:8px;line-height:20px;text-align:left;vertical-align:top;border-top:1px solid #aaaaaa;}
.table th{font-weight:bold;}
.table thead th{vertical-align:bottom;}
.table caption+thead tr:first-child th,.table caption+thead tr:first-child td,.table colgroup+thead tr:first-child th,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child th,.table thead:first-child tr:first-child td{border-top:0;}
.table tbody+tbody{border-top:2px solid #aaaaaa;}
.table .table{background-color:#ffffff;}
.table-condensed th,.table-condensed td{padding:4px 5px;}
.table-bordered{border:1px solid #aaaaaa;border-collapse:separate;*border-collapse:collapse;border-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.table-bordered th,.table-bordered td{border-left:1px solid #aaaaaa;}
.table-bordered caption+thead tr:first-child th,.table-bordered caption+tbody tr:first-child th,.table-bordered caption+tbody tr:first-child td,.table-bordered colgroup+thead tr:first-child th,.table-bordered colgroup+tbody tr:first-child th,.table-bordered colgroup+tbody tr:first-child td,.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0;}
.table-bordered thead:first-child tr:first-child>th:first-child,.table-bordered tbody:first-child tr:first-child>td:first-child,.table-bordered tbody:first-child tr:first-child>th:first-child{-webkit-border-top-left-radius:4px;-moz-border-radius-topleft:4px;border-top-left-radius:4px;}
.table-bordered thead:first-child tr:first-child>th:last-child,.table-bordered tbody:first-child tr:first-child>td:last-child,.table-bordered tbody:first-child tr:first-child>th:last-child{-webkit-border-top-right-radius:4px;-moz-border-radius-topright:4px;border-top-right-radius:4px;}
.table-bordered thead:last-child tr:last-child>th:first-child,.table-bordered tbody:last-child tr:last-child>td:first-child,.table-bordered tbody:last-child tr:last-child>th:first-child,.table-bordered tfoot:last-child tr:last-child>td:first-child,.table-bordered tfoot:last-child tr:last-child>th:first-child{-webkit-border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;border-bottom-left-radius:4px;}
.table-bordered thead:last-child tr:last-child>th:last-child,.table-bordered tbody:last-child tr:last-child>td:last-child,.table-bordered tbody:last-child tr:last-child>th:last-child,.table-bordered tfoot:last-child tr:last-child>td:last-child,.table-bordered tfoot:last-child tr:last-child>th:last-child{-webkit-border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px;border-bottom-right-radius:4px;}
.table-bordered tfoot+tbody:last-child tr:last-child td:first-child{-webkit-border-bottom-left-radius:0;-moz-border-radius-bottomleft:0;border-bottom-left-radius:0;}
.table-bordered tfoot+tbody:last-child tr:last-child td:last-child{-webkit-border-bottom-right-radius:0;-moz-border-radius-bottomright:0;border-bottom-right-radius:0;}
.table-bordered caption+thead tr:first-child th:first-child,.table-bordered caption+tbody tr:first-child td:first-child,.table-bordered colgroup+thead tr:first-child th:first-child,.table-bordered colgroup+tbody tr:first-child td:first-child{-webkit-border-top-left-radius:4px;-moz-border-radius-topleft:4px;border-top-left-radius:4px;}
.table-bordered caption+thead tr:first-child th:last-child,.table-bordered caption+tbody tr:first-child td:last-child,.table-bordered colgroup+thead tr:first-child th:last-child,.table-bordered colgroup+tbody tr:first-child td:last-child{-webkit-border-top-right-radius:4px;-moz-border-radius-topright:4px;border-top-right-radius:4px;}
.table-striped tbody>tr:nth-child(odd)>td,.table-striped tbody>tr:nth-child(odd)>th{background-color:#f9f9f9;}
.table-hover tbody tr:hover>td,.table-hover tbody tr:hover>th{background-color:#f5f5f5;}
table td[class*="span"],table th[class*="span"],.row-fluid table td[class*="span"],.row-fluid table th[class*="span"]{display:table-cell;float:none;margin-left:0;}
.table td.span1,.table th.span1{float:none;width:44px;margin-left:0;}
.table td.span2,.table th.span2{float:none;width:124px;margin-left:0;}
.table td.span3,.table th.span3{float:none;width:204px;margin-left:0;}
.table td.span4,.table th.span4{float:none;width:284px;margin-left:0;}
.table td.span5,.table th.span5{float:none;width:364px;margin-left:0;}
.table td.span6,.table th.span6{float:none;width:444px;margin-left:0;}
.table td.span7,.table th.span7{float:none;width:524px;margin-left:0;}
.table td.span8,.table th.span8{float:none;width:604px;margin-left:0;}
.table td.span9,.table th.span9{float:none;width:684px;margin-left:0;}
.table td.span10,.table th.span10{float:none;width:764px;margin-left:0;}
.table td.span11,.table th.span11{float:none;width:844px;margin-left:0;}
.table td.span12,.table th.span12{float:none;width:924px;margin-left:0;}
.table tbody tr.success>td{background-color:#dff0d8;}
.table tbody tr.error>td{background-color:#f2dede;}
.table tbody tr.warning>td{background-color:#fcf8e3;}
.table tbody tr.info>td{background-color:#d9edf7;}
.table-hover tbody tr.success:hover>td{background-color:#d0e9c6;}
.table-hover tbody tr.error:hover>td{background-color:#ebcccc;}
.table-hover tbody tr.warning:hover>td{background-color:#faf2cc;}
.table-hover tbody tr.info:hover>td{background-color:#c4e3f3;}
[class^="icon-"],[class*=" icon-"]{display:inline-block;width:14px;height:14px;*margin-right:.3em;line-height:14px;vertical-align:text-top;background-image:url("../img/glyphicons-halflings.png");background-position:14px 14px;background-repeat:no-repeat;margin-top:1px;}
.icon-white,.nav-pills>.active>a>[class^="icon-"],.nav-pills>.active>a>[class*=" icon-"],.nav-list>.active>a>[class^="icon-"],.nav-list>.active>a>[class*=" icon-"],.navbar-inverse .nav>.active>a>[class^="icon-"],.navbar-inverse .nav>.active>a>[class*=" icon-"],.dropdown-menu>li>a:hover>[class^="icon-"],.dropdown-menu>li>a:focus>[class^="icon-"],.dropdown-menu>li>a:hover>[class*=" icon-"],.dropdown-menu>li>a:focus>[class*=" icon-"],.dropdown-menu>.active>a>[class^="icon-"],.dropdown-menu>.active>a>[class*=" icon-"],.dropdown-submenu:hover>a>[class^="icon-"],.dropdown-submenu:focus>a>[class^="icon-"],.dropdown-submenu:hover>a>[class*=" icon-"],.dropdown-submenu:focus>a>[class*=" icon-"]{background-image:url("../img/glyphicons-halflings-white.png");}
.icon-glass{background-position:0 0;}
.icon-music{background-position:-24px 0;}
.icon-search{background-position:-48px 0;}
.icon-envelope{background-position:-72px 0;}
.icon-heart{background-position:-96px 0;}
.icon-star{background-position:-120px 0;}
.icon-star-empty{background-position:-144px 0;}
.icon-user{background-position:-168px 0;}
.icon-film{background-position:-192px 0;}
.icon-th-large{background-position:-216px 0;}
.icon-th{background-position:-240px 0;}
.icon-th-list{background-position:-264px 0;}
.icon-ok{background-position:-288px 0;}
.icon-remove{background-position:-312px 0;}
.icon-zoom-in{background-position:-336px 0;}
.icon-zoom-out{background-position:-360px 0;}
.icon-off{background-position:-384px 0;}
.icon-signal{background-position:-408px 0;}
.icon-cog{background-position:-432px 0;}
.icon-trash{background-position:-456px 0;}
.icon-home{background-position:0 -24px;}
.icon-file{background-position:-24px -24px;}
.icon-time{background-position:-48px -24px;}
.icon-road{background-position:-72px -24px;}
.icon-download-alt{background-position:-96px -24px;}
.icon-download{background-position:-120px -24px;}
.icon-upload{background-position:-144px -24px;}
.icon-inbox{background-position:-168px -24px;}
.icon-play-circle{background-position:-192px -24px;}
.icon-repeat{background-position:-216px -24px;}
.icon-refresh{background-position:-240px -24px;}
.icon-list-alt{background-position:-264px -24px;}
.icon-lock{background-position:-287px -24px;}
.icon-flag{background-position:-312px -24px;}
.icon-headphones{background-position:-336px -24px;}
.icon-volume-off{background-position:-360px -24px;}
.icon-volume-down{background-position:-384px -24px;}
.icon-volume-up{background-position:-408px -24px;}
.icon-qrcode{background-position:-432px -24px;}
.icon-barcode{background-position:-456px -24px;}
.icon-tag{background-position:0 -48px;}
.icon-tags{background-position:-25px -48px;}
.icon-book{background-position:-48px -48px;}
.icon-bookmark{background-position:-72px -48px;}
.icon-print{background-position:-96px -48px;}
.icon-camera{background-position:-120px -48px;}
.icon-font{background-position:-144px -48px;}
.icon-bold{background-position:-167px -48px;}
.icon-italic{background-position:-192px -48px;}
.icon-text-height{background-position:-216px -48px;}
.icon-text-width{background-position:-240px -48px;}
.icon-align-left{background-position:-264px -48px;}
.icon-align-center{background-position:-288px -48px;}
.icon-align-right{background-position:-312px -48px;}
.icon-align-justify{background-position:-336px -48px;}
.icon-list{background-position:-360px -48px;}
.icon-indent-left{background-position:-384px -48px;}
.icon-indent-right{background-position:-408px -48px;}
.icon-facetime-video{background-position:-432px -48px;}
.icon-picture{background-position:-456px -48px;}
.icon-pencil{background-position:0 -72px;}
.icon-map-marker{background-position:-24px -72px;}
.icon-adjust{background-position:-48px -72px;}
.icon-tint{background-position:-72px -72px;}
.icon-edit{background-position:-96px -72px;}
.icon-share{background-position:-120px -72px;}
.icon-check{background-position:-144px -72px;}
.icon-move{background-position:-168px -72px;}
.icon-step-backward{background-position:-192px -72px;}
.icon-fast-backward{background-position:-216px -72px;}
.icon-backward{background-position:-240px -72px;}
.icon-play{background-position:-264px -72px;}
.icon-pause{background-position:-288px -72px;}
.icon-stop{background-position:-312px -72px;}
.icon-forward{background-position:-336px -72px;}
.icon-fast-forward{background-position:-360px -72px;}
.icon-step-forward{background-position:-384px -72px;}
.icon-eject{background-position:-408px -72px;}
.icon-chevron-left{background-position:-432px -72px;}
.icon-chevron-right{background-position:-456px -72px;}
.icon-plus-sign{background-position:0 -96px;}
.icon-minus-sign{background-position:-24px -96px;}
.icon-remove-sign{background-position:-48px -96px;}
.icon-ok-sign{background-position:-72px -96px;}
.icon-question-sign{background-position:-96px -96px;}
.icon-info-sign{background-position:-120px -96px;}
.icon-screenshot{background-position:-144px -96px;}
.icon-remove-circle{background-position:-168px -96px;}
.icon-ok-circle{background-position:-192px -96px;}
.icon-ban-circle{background-position:-216px -96px;}
.icon-arrow-left{background-position:-240px -96px;}
.icon-arrow-right{background-position:-264px -96px;}
.icon-arrow-up{background-position:-289px -96px;}
.icon-arrow-down{background-position:-312px -96px;}
.icon-share-alt{background-position:-336px -96px;}
.icon-resize-full{background-position:-360px -96px;}
.icon-resize-small{background-position:-384px -96px;}
.icon-plus{background-position:-408px -96px;}
.icon-minus{background-position:-433px -96px;}
.icon-asterisk{background-position:-456px -96px;}
.icon-exclamation-sign{background-position:0 -120px;}
.icon-gift{background-position:-24px -120px;}
.icon-leaf{background-position:-48px -120px;}
.icon-fire{background-position:-72px -120px;}
.icon-eye-open{background-position:-96px -120px;}
.icon-eye-close{background-position:-120px -120px;}
.icon-warning-sign{background-position:-144px -120px;}
.icon-plane{background-position:-168px -120px;}
.icon-calendar{background-position:-192px -120px;}
.icon-random{background-position:-216px -120px;width:16px;}
.icon-comment{background-position:-240px -120px;}
.icon-magnet{background-position:-264px -120px;}
.icon-chevron-up{background-position:-288px -120px;}
.icon-chevron-down{background-position:-313px -119px;}
.icon-retweet{background-position:-336px -120px;}
.icon-shopping-cart{background-position:-360px -120px;}
.icon-folder-close{background-position:-384px -120px;width:16px;}
.icon-folder-open{background-position:-408px -120px;width:16px;}
.icon-resize-vertical{background-position:-432px -119px;}
.icon-resize-horizontal{background-position:-456px -118px;}
.icon-hdd{background-position:0 -144px;}
.icon-bullhorn{background-position:-24px -144px;}
.icon-bell{background-position:-48px -144px;}
.icon-certificate{background-position:-72px -144px;}
.icon-thumbs-up{background-position:-96px -144px;}
.icon-thumbs-down{background-position:-120px -144px;}
.icon-hand-right{background-position:-144px -144px;}
.icon-hand-left{background-position:-168px -144px;}
.icon-hand-up{background-position:-192px -144px;}
.icon-hand-down{background-position:-216px -144px;}
.icon-circle-arrow-right{background-position:-240px -144px;}
.icon-circle-arrow-left{background-position:-264px -144px;}
.icon-circle-arrow-up{background-position:-288px -144px;}
.icon-circle-arrow-down{background-position:-312px -144px;}
.icon-globe{background-position:-336px -144px;}
.icon-wrench{background-position:-360px -144px;}
.icon-tasks{background-position:-384px -144px;}
.icon-filter{background-position:-408px -144px;}
.icon-briefcase{background-position:-432px -144px;}
.icon-fullscreen{background-position:-456px -144px;}
.dropup,.dropdown{position:relative;}
.dropdown-toggle{*margin-bottom:-3px;}
.dropdown-toggle:active,.open .dropdown-toggle{outline:0;}
.caret{display:inline-block;width:0;height:0;vertical-align:top;border-top:4px solid #000000;border-right:4px solid transparent;border-left:4px solid transparent;content:"";}
.dropdown .caret{margin-top:8px;margin-left:2px;}
.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;background-color:#ffffff;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.2);*border-right-width:2px;*border-bottom-width:2px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box;}.dropdown-menu.pull-right{right:0;left:auto;}
.dropdown-menu .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #ffffff;}
.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:20px;color:#333333;white-space:nowrap;}
.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus,.dropdown-submenu:hover>a,.dropdown-submenu:focus>a{text-decoration:none;color:#ffffff;background-color:#dc6200;background-image:-moz-linear-gradient(top, #e66700, #cd5c00);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#e66700), to(#cd5c00));background-image:-webkit-linear-gradient(top, #e66700, #cd5c00);background-image:-o-linear-gradient(top, #e66700, #cd5c00);background-image:linear-gradient(to bottom, #e66700, #cd5c00);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe66700', endColorstr='#ffcd5c00', GradientType=0);}
.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#ffffff;text-decoration:none;outline:0;background-color:#dc6200;background-image:-moz-linear-gradient(top, #e66700, #cd5c00);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#e66700), to(#cd5c00));background-image:-webkit-linear-gradient(top, #e66700, #cd5c00);background-image:-o-linear-gradient(top, #e66700, #cd5c00);background-image:linear-gradient(to bottom, #e66700, #cd5c00);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe66700', endColorstr='#ffcd5c00', GradientType=0);}
.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#4d4d4d;}
.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);cursor:default;}
.open{*z-index:1000;}.open>.dropdown-menu{display:block;}
.pull-right>.dropdown-menu{right:0;left:auto;}
.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000000;content:"";}
.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px;}
.dropdown-submenu{position:relative;}
.dropdown-submenu>.dropdown-menu{top:0;left:100%;margin-top:-6px;margin-left:-1px;-webkit-border-radius:0 6px 6px 6px;-moz-border-radius:0 6px 6px 6px;border-radius:0 6px 6px 6px;}
.dropdown-submenu:hover>.dropdown-menu{display:block;}
.dropup .dropdown-submenu>.dropdown-menu{top:auto;bottom:0;margin-top:0;margin-bottom:-2px;-webkit-border-radius:5px 5px 5px 0;-moz-border-radius:5px 5px 5px 0;border-radius:5px 5px 5px 0;}
.dropdown-submenu>a:after{display:block;content:" ";float:right;width:0;height:0;border-color:transparent;border-style:solid;border-width:5px 0 5px 5px;border-left-color:#cccccc;margin-top:5px;margin-right:-10px;}
.dropdown-submenu:hover>a:after{border-left-color:#ffffff;}
.dropdown-submenu.pull-left{float:none;}.dropdown-submenu.pull-left>.dropdown-menu{left:-100%;margin-left:10px;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px;}
.dropdown .dropdown-menu .nav-header{padding-left:20px;padding-right:20px;}
.typeahead{z-index:1051;margin-top:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);}.well blockquote{border-color:#ddd;border-color:rgba(0, 0, 0, 0.15);}
.well-large{padding:24px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}
.well-small{padding:9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
.fade{opacity:0;-webkit-transition:opacity 0.15s linear;-moz-transition:opacity 0.15s linear;-o-transition:opacity 0.15s linear;transition:opacity 0.15s linear;}.fade.in{opacity:1;}
.collapse{position:relative;height:0;overflow:hidden;-webkit-transition:height 0.35s ease;-moz-transition:height 0.35s ease;-o-transition:height 0.35s ease;transition:height 0.35s ease;}.collapse.in{height:auto;}
.close{float:right;font-size:20px;font-weight:bold;line-height:20px;color:#000000;text-shadow:0 1px 0 #ffffff;opacity:0.2;filter:alpha(opacity=20);}.close:hover,.close:focus{color:#000000;text-decoration:none;cursor:pointer;opacity:0.4;filter:alpha(opacity=40);}
button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none;}
.btn{display:inline-block;*display:inline;*zoom:1;padding:4px 12px;margin-bottom:0;font-size:14px;line-height:20px;text-align:center;vertical-align:middle;cursor:pointer;color:#333333;text-shadow:0 1px 1px rgba(255, 255, 255, 0.75);background-color:#f5f5f5;background-image:-moz-linear-gradient(top, #ffffff, #e6e6e6);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6));background-image:-webkit-linear-gradient(top, #ffffff, #e6e6e6);background-image:-o-linear-gradient(top, #ffffff, #e6e6e6);background-image:linear-gradient(to bottom, #ffffff, #e6e6e6);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0);border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#e6e6e6;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);border:1px solid #cccccc;*border:0;border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*margin-left:.3em;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);}.btn:hover,.btn:focus,.btn:active,.btn.active,.btn.disabled,.btn[disabled]{color:#333333;background-color:#e6e6e6;*background-color:#d9d9d9;}
.btn:active,.btn.active{background-color:#cccccc \9;}
.btn:first-child{*margin-left:0;}
.btn:hover,.btn:focus{color:#333333;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position 0.1s linear;-moz-transition:background-position 0.1s linear;-o-transition:background-position 0.1s linear;transition:background-position 0.1s linear;}
.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;}
.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05);}
.btn.disabled,.btn[disabled]{cursor:default;background-image:none;opacity:0.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
.btn-large{padding:11px 19px;font-size:17.5px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}
.btn-large [class^="icon-"],.btn-large [class*=" icon-"]{margin-top:4px;}
.btn-small{padding:2px 10px;font-size:11.9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
.btn-small [class^="icon-"],.btn-small [class*=" icon-"]{margin-top:0;}
.btn-mini [class^="icon-"],.btn-mini [class*=" icon-"]{margin-top:-1px;}
.btn-mini{padding:0 6px;font-size:10.5px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
.btn-block{display:block;width:100%;padding-left:0;padding-right:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;}
.btn-block+.btn-block{margin-top:5px;}
input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%;}
.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-inverse.active{color:rgba(255, 255, 255, 0.75);}
.btn-primary{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#e68600;background-image:-moz-linear-gradient(top, #e66700, #e6b400);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#e66700), to(#e6b400));background-image:-webkit-linear-gradient(top, #e66700, #e6b400);background-image:-o-linear-gradient(top, #e66700, #e6b400);background-image:linear-gradient(to bottom, #e66700, #e6b400);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe66700', endColorstr='#ffe6b400', GradientType=0);border-color:#e6b400 #e6b400 #9a7800;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#e6b400;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-primary:hover,.btn-primary:focus,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{color:#ffffff;background-color:#e6b400;*background-color:#cda000;}
.btn-primary:active,.btn-primary.active{background-color:#b38c00 \9;}
.btn-warning{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#faa732;background-image:-moz-linear-gradient(top, #fbb450, #f89406);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));background-image:-webkit-linear-gradient(top, #fbb450, #f89406);background-image:-o-linear-gradient(top, #fbb450, #f89406);background-image:linear-gradient(to bottom, #fbb450, #f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0);border-color:#f89406 #f89406 #ad6704;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#f89406;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-warning:hover,.btn-warning:focus,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{color:#ffffff;background-color:#f89406;*background-color:#df8505;}
.btn-warning:active,.btn-warning.active{background-color:#c67605 \9;}
.btn-danger{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#da4f49;background-image:-moz-linear-gradient(top, #ee5f5b, #bd362f);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f));background-image:-webkit-linear-gradient(top, #ee5f5b, #bd362f);background-image:-o-linear-gradient(top, #ee5f5b, #bd362f);background-image:linear-gradient(to bottom, #ee5f5b, #bd362f);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffbd362f', GradientType=0);border-color:#bd362f #bd362f #802420;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#bd362f;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-danger:hover,.btn-danger:focus,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{color:#ffffff;background-color:#bd362f;*background-color:#a9302a;}
.btn-danger:active,.btn-danger.active{background-color:#942a25 \9;}
.btn-success{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#5bb75b;background-image:-moz-linear-gradient(top, #62c462, #51a351);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));background-image:-webkit-linear-gradient(top, #62c462, #51a351);background-image:-o-linear-gradient(top, #62c462, #51a351);background-image:linear-gradient(to bottom, #62c462, #51a351);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff51a351', GradientType=0);border-color:#51a351 #51a351 #387038;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#51a351;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-success:hover,.btn-success:focus,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{color:#ffffff;background-color:#51a351;*background-color:#499249;}
.btn-success:active,.btn-success.active{background-color:#408140 \9;}
.btn-info{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#49afcd;background-image:-moz-linear-gradient(top, #5bc0de, #2f96b4);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4));background-image:-webkit-linear-gradient(top, #5bc0de, #2f96b4);background-image:-o-linear-gradient(top, #5bc0de, #2f96b4);background-image:linear-gradient(to bottom, #5bc0de, #2f96b4);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2f96b4', GradientType=0);border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#2f96b4;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-info:hover,.btn-info:focus,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{color:#ffffff;background-color:#2f96b4;*background-color:#2a85a0;}
.btn-info:active,.btn-info.active{background-color:#24748c \9;}
.btn-inverse{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#333333;background-image:-moz-linear-gradient(top, #444444, #1a1a1a);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#444444), to(#1a1a1a));background-image:-webkit-linear-gradient(top, #444444, #1a1a1a);background-image:-o-linear-gradient(top, #444444, #1a1a1a);background-image:linear-gradient(to bottom, #444444, #1a1a1a);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444', endColorstr='#ff1a1a1a', GradientType=0);border-color:#1a1a1a #1a1a1a #000000;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#1a1a1a;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-inverse:hover,.btn-inverse:focus,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{color:#ffffff;background-color:#1a1a1a;*background-color:#0d0d0d;}
.btn-inverse:active,.btn-inverse.active{background-color:#000000 \9;}
button.btn,input[type="submit"].btn{*padding-top:3px;*padding-bottom:3px;}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0;}
button.btn.btn-large,input[type="submit"].btn.btn-large{*padding-top:7px;*padding-bottom:7px;}
button.btn.btn-small,input[type="submit"].btn.btn-small{*padding-top:3px;*padding-bottom:3px;}
button.btn.btn-mini,input[type="submit"].btn.btn-mini{*padding-top:1px;*padding-bottom:1px;}
.btn-link,.btn-link:active,.btn-link[disabled]{background-color:transparent;background-image:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
.btn-link{border-color:transparent;cursor:pointer;color:#e66700;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
.btn-link:hover,.btn-link:focus{color:#9a4500;text-decoration:underline;background-color:transparent;}
.btn-link[disabled]:hover,.btn-link[disabled]:focus{color:#333333;text-decoration:none;}
.btn-group{position:relative;display:inline-block;*display:inline;*zoom:1;font-size:0;vertical-align:middle;white-space:nowrap;*margin-left:.3em;}.btn-group:first-child{*margin-left:0;}
.btn-group+.btn-group{margin-left:5px;}
.btn-toolbar{font-size:0;margin-top:10px;margin-bottom:10px;}.btn-toolbar>.btn+.btn,.btn-toolbar>.btn-group+.btn,.btn-toolbar>.btn+.btn-group{margin-left:5px;}
.btn-group>.btn{position:relative;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
.btn-group>.btn+.btn{margin-left:-1px;}
.btn-group>.btn,.btn-group>.dropdown-menu,.btn-group>.popover{font-size:14px;}
.btn-group>.btn-mini{font-size:10.5px;}
.btn-group>.btn-small{font-size:11.9px;}
.btn-group>.btn-large{font-size:17.5px;}
.btn-group>.btn:first-child{margin-left:0;-webkit-border-top-left-radius:4px;-moz-border-radius-topleft:4px;border-top-left-radius:4px;-webkit-border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;border-bottom-left-radius:4px;}
.btn-group>.btn:last-child,.btn-group>.dropdown-toggle{-webkit-border-top-right-radius:4px;-moz-border-radius-topright:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px;border-bottom-right-radius:4px;}
.btn-group>.btn.large:first-child{margin-left:0;-webkit-border-top-left-radius:6px;-moz-border-radius-topleft:6px;border-top-left-radius:6px;-webkit-border-bottom-left-radius:6px;-moz-border-radius-bottomleft:6px;border-bottom-left-radius:6px;}
.btn-group>.btn.large:last-child,.btn-group>.large.dropdown-toggle{-webkit-border-top-right-radius:6px;-moz-border-radius-topright:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;-moz-border-radius-bottomright:6px;border-bottom-right-radius:6px;}
.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active{z-index:2;}
.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0;}
.btn-group>.btn+.dropdown-toggle{padding-left:8px;padding-right:8px;-webkit-box-shadow:inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);box-shadow:inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);*padding-top:5px;*padding-bottom:5px;}
.btn-group>.btn-mini+.dropdown-toggle{padding-left:5px;padding-right:5px;*padding-top:2px;*padding-bottom:2px;}
.btn-group>.btn-small+.dropdown-toggle{*padding-top:5px;*padding-bottom:4px;}
.btn-group>.btn-large+.dropdown-toggle{padding-left:12px;padding-right:12px;*padding-top:7px;*padding-bottom:7px;}
.btn-group.open .dropdown-toggle{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05);}
.btn-group.open .btn.dropdown-toggle{background-color:#e6e6e6;}
.btn-group.open .btn-primary.dropdown-toggle{background-color:#e6b400;}
.btn-group.open .btn-warning.dropdown-toggle{background-color:#f89406;}
.btn-group.open .btn-danger.dropdown-toggle{background-color:#bd362f;}
.btn-group.open .btn-success.dropdown-toggle{background-color:#51a351;}
.btn-group.open .btn-info.dropdown-toggle{background-color:#2f96b4;}
.btn-group.open .btn-inverse.dropdown-toggle{background-color:#1a1a1a;}
.btn .caret{margin-top:8px;margin-left:0;}
.btn-large .caret{margin-top:6px;}
.btn-large .caret{border-left-width:5px;border-right-width:5px;border-top-width:5px;}
.btn-mini .caret,.btn-small .caret{margin-top:8px;}
.dropup .btn-large .caret{border-bottom-width:5px;}
.btn-primary .caret,.btn-warning .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret,.btn-inverse .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;}
.btn-group-vertical{display:inline-block;*display:inline;*zoom:1;}
.btn-group-vertical>.btn{display:block;float:none;max-width:100%;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
.btn-group-vertical>.btn+.btn{margin-left:0;margin-top:-1px;}
.btn-group-vertical>.btn:first-child{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;}
.btn-group-vertical>.btn:last-child{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;}
.btn-group-vertical>.btn-large:first-child{-webkit-border-radius:6px 6px 0 0;-moz-border-radius:6px 6px 0 0;border-radius:6px 6px 0 0;}
.btn-group-vertical>.btn-large:last-child{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;}
.alert{padding:8px 35px 8px 14px;margin-bottom:20px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
.alert,.alert h4{color:#c09853;}
.alert h4{margin:0;}
.alert .close{position:relative;top:-2px;right:-21px;line-height:20px;}
.alert-success{background-color:#dff0d8;border-color:#d6e9c6;color:#468847;}
.alert-success h4{color:#468847;}
.alert-danger,.alert-error{background-color:#f2dede;border-color:#eed3d7;color:#b94a48;}
.alert-danger h4,.alert-error h4{color:#b94a48;}
.alert-info{background-color:#d9edf7;border-color:#bce8f1;color:#3a87ad;}
.alert-info h4{color:#3a87ad;}
.alert-block{padding-top:14px;padding-bottom:14px;}
.alert-block>p,.alert-block>ul{margin-bottom:0;}
.alert-block p+p{margin-top:5px;}
.nav{margin-left:0;margin-bottom:20px;list-style:none;}
.nav>li>a{display:block;}
.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eeeeee;}
.nav>li>a>img{max-width:none;}
.nav>.pull-right{float:right;}
.nav-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:20px;color:#4d4d4d;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);text-transform:uppercase;}
.nav li+.nav-header{margin-top:9px;}
.nav-list{padding-left:15px;padding-right:15px;margin-bottom:0;}
.nav-list>li>a,.nav-list .nav-header{margin-left:-15px;margin-right:-15px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);}
.nav-list>li>a{padding:3px 15px;}
.nav-list>.active>a,.nav-list>.active>a:hover,.nav-list>.active>a:focus{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.2);background-color:#e66700;}
.nav-list [class^="icon-"],.nav-list [class*=" icon-"]{margin-right:2px;}
.nav-list .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #ffffff;}
.nav-tabs,.nav-pills{*zoom:1;}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;content:"";line-height:0;}
.nav-tabs:after,.nav-pills:after{clear:both;}
.nav-tabs>li,.nav-pills>li{float:left;}
.nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px;}
.nav-tabs{border-bottom:1px solid #ddd;}
.nav-tabs>li{margin-bottom:-1px;}
.nav-tabs>li>a{padding-top:8px;padding-bottom:8px;line-height:20px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;}.nav-tabs>li>a:hover,.nav-tabs>li>a:focus{border-color:#aaaaaa #aaaaaa #dddddd;}
.nav-tabs>.active>a,.nav-tabs>.active>a:hover,.nav-tabs>.active>a:focus{color:#333333;background-color:#ffffff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default;}
.nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;}
.nav-pills>.active>a,.nav-pills>.active>a:hover,.nav-pills>.active>a:focus{color:#ffffff;background-color:#e66700;}
.nav-stacked>li{float:none;}
.nav-stacked>li>a{margin-right:0;}
.nav-tabs.nav-stacked{border-bottom:0;}
.nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
.nav-tabs.nav-stacked>li:first-child>a{-webkit-border-top-right-radius:4px;-moz-border-radius-topright:4px;border-top-right-radius:4px;-webkit-border-top-left-radius:4px;-moz-border-radius-topleft:4px;border-top-left-radius:4px;}
.nav-tabs.nav-stacked>li:last-child>a{-webkit-border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px;border-bottom-right-radius:4px;-webkit-border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;border-bottom-left-radius:4px;}
.nav-tabs.nav-stacked>li>a:hover,.nav-tabs.nav-stacked>li>a:focus{border-color:#ddd;z-index:2;}
.nav-pills.nav-stacked>li>a{margin-bottom:3px;}
.nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px;}
.nav-tabs .dropdown-menu{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;}
.nav-pills .dropdown-menu{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}
.nav .dropdown-toggle .caret{border-top-color:#e66700;border-bottom-color:#e66700;margin-top:6px;}
.nav .dropdown-toggle:hover .caret,.nav .dropdown-toggle:focus .caret{border-top-color:#9a4500;border-bottom-color:#9a4500;}
.nav-tabs .dropdown-toggle .caret{margin-top:8px;}
.nav .active .dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff;}
.nav-tabs .active .dropdown-toggle .caret{border-top-color:#333333;border-bottom-color:#333333;}
.nav>.dropdown.active>a:hover,.nav>.dropdown.active>a:focus{cursor:pointer;}
.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>li.dropdown.open.active>a:hover,.nav>li.dropdown.open.active>a:focus{color:#ffffff;background-color:#4d4d4d;border-color:#4d4d4d;}
.nav li.dropdown.open .caret,.nav li.dropdown.open.active .caret,.nav li.dropdown.open a:hover .caret,.nav li.dropdown.open a:focus .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;opacity:1;filter:alpha(opacity=100);}
.tabs-stacked .open>a:hover,.tabs-stacked .open>a:focus{border-color:#4d4d4d;}
.tabbable{*zoom:1;}.tabbable:before,.tabbable:after{display:table;content:"";line-height:0;}
.tabbable:after{clear:both;}
.tab-content{overflow:auto;}
.tabs-below>.nav-tabs,.tabs-right>.nav-tabs,.tabs-left>.nav-tabs{border-bottom:0;}
.tab-content>.tab-pane,.pill-content>.pill-pane{display:none;}
.tab-content>.active,.pill-content>.active{display:block;}
.tabs-below>.nav-tabs{border-top:1px solid #ddd;}
.tabs-below>.nav-tabs>li{margin-top:-1px;margin-bottom:0;}
.tabs-below>.nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;}.tabs-below>.nav-tabs>li>a:hover,.tabs-below>.nav-tabs>li>a:focus{border-bottom-color:transparent;border-top-color:#ddd;}
.tabs-below>.nav-tabs>.active>a,.tabs-below>.nav-tabs>.active>a:hover,.tabs-below>.nav-tabs>.active>a:focus{border-color:transparent #ddd #ddd #ddd;}
.tabs-left>.nav-tabs>li,.tabs-right>.nav-tabs>li{float:none;}
.tabs-left>.nav-tabs>li>a,.tabs-right>.nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px;}
.tabs-left>.nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd;}
.tabs-left>.nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px;}
.tabs-left>.nav-tabs>li>a:hover,.tabs-left>.nav-tabs>li>a:focus{border-color:#aaaaaa #dddddd #aaaaaa #aaaaaa;}
.tabs-left>.nav-tabs .active>a,.tabs-left>.nav-tabs .active>a:hover,.tabs-left>.nav-tabs .active>a:focus{border-color:#ddd transparent #ddd #ddd;*border-right-color:#ffffff;}
.tabs-right>.nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd;}
.tabs-right>.nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0;}
.tabs-right>.nav-tabs>li>a:hover,.tabs-right>.nav-tabs>li>a:focus{border-color:#aaaaaa #aaaaaa #aaaaaa #dddddd;}
.tabs-right>.nav-tabs .active>a,.tabs-right>.nav-tabs .active>a:hover,.tabs-right>.nav-tabs .active>a:focus{border-color:#ddd #ddd #ddd transparent;*border-left-color:#ffffff;}
.nav>.disabled>a{color:#4d4d4d;}
.nav>.disabled>a:hover,.nav>.disabled>a:focus{text-decoration:none;background-color:transparent;cursor:default;}
.navbar{overflow:visible;margin-bottom:20px;*position:relative;*z-index:2;}
.navbar-inner{min-height:40px;padding-left:20px;padding-right:20px;background-color:#2e2e2e;background-image:-moz-linear-gradient(top, #404040, #262626);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#404040), to(#262626));background-image:-webkit-linear-gradient(top, #404040, #262626);background-image:-o-linear-gradient(top, #404040, #262626);background-image:linear-gradient(to bottom, #404040, #262626);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff404040', endColorstr='#ff262626', GradientType=0);border:1px solid #080808;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 4px rgba(0, 0, 0, 0.065);-moz-box-shadow:0 1px 4px rgba(0, 0, 0, 0.065);box-shadow:0 1px 4px rgba(0, 0, 0, 0.065);*zoom:1;}.navbar-inner:before,.navbar-inner:after{display:table;content:"";line-height:0;}
.navbar-inner:after{clear:both;}
.navbar .container{width:auto;}
.nav-collapse.collapse{height:auto;overflow:visible;}
.navbar .brand{float:left;display:block;padding:10px 20px 10px;margin-left:-20px;font-size:20px;font-weight:200;color:#ffffff;text-shadow:0 1px 0 #333333;}.navbar .brand:hover,.navbar .brand:focus{text-decoration:none;}
.navbar-text{margin-bottom:0;line-height:40px;color:#ffffff;}
.navbar-link{color:#ffffff;}.navbar-link:hover,.navbar-link:focus{color:#aaaaaa;}
.navbar .divider-vertical{height:40px;margin:0 9px;border-left:1px solid #262626;border-right:1px solid #333333;}
.navbar .btn,.navbar .btn-group{margin-top:5px;}
.navbar .btn-group .btn,.navbar .input-prepend .btn,.navbar .input-append .btn,.navbar .input-prepend .btn-group,.navbar .input-append .btn-group{margin-top:0;}
.navbar-form{margin-bottom:0;*zoom:1;}.navbar-form:before,.navbar-form:after{display:table;content:"";line-height:0;}
.navbar-form:after{clear:both;}
.navbar-form input,.navbar-form select,.navbar-form .radio,.navbar-form .checkbox{margin-top:5px;}
.navbar-form input,.navbar-form select,.navbar-form .btn{display:inline-block;margin-bottom:0;}
.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px;}
.navbar-form .input-append,.navbar-form .input-prepend{margin-top:5px;white-space:nowrap;}.navbar-form .input-append input,.navbar-form .input-prepend input{margin-top:0;}
.navbar-search{position:relative;float:left;margin-top:5px;margin-bottom:0;}.navbar-search .search-query{margin-bottom:0;padding:4px 14px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px;}
.navbar-static-top{position:static;margin-bottom:0;}.navbar-static-top .navbar-inner{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;margin-bottom:0;}
.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{border-width:0 0 1px;}
.navbar-fixed-bottom .navbar-inner{border-width:1px 0 0;}
.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding-left:0;padding-right:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px;}
.navbar-fixed-top{top:0;}
.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{-webkit-box-shadow:0 1px 10px rgba(0,0,0,.1);-moz-box-shadow:0 1px 10px rgba(0,0,0,.1);box-shadow:0 1px 10px rgba(0,0,0,.1);}
.navbar-fixed-bottom{bottom:0;}.navbar-fixed-bottom .navbar-inner{-webkit-box-shadow:0 -1px 10px rgba(0,0,0,.1);-moz-box-shadow:0 -1px 10px rgba(0,0,0,.1);box-shadow:0 -1px 10px rgba(0,0,0,.1);}
.navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0;}
.navbar .nav.pull-right{float:right;margin-right:0;}
.navbar .nav>li{float:left;}
.navbar .nav>li>a{float:none;padding:10px 15px 10px;color:#ffffff;text-decoration:none;text-shadow:0 1px 0 #333333;}
.navbar .nav .dropdown-toggle .caret{margin-top:8px;}
.navbar .nav>li>a:focus,.navbar .nav>li>a:hover{background-color:transparent;color:#aaaaaa;text-decoration:none;}
.navbar .nav>.active>a,.navbar .nav>.active>a:hover,.navbar .nav>.active>a:focus{color:#aaaaaa;text-decoration:none;background-color:#1a1a1a;-webkit-box-shadow:inset 0 3px 8px rgba(0, 0, 0, 0.125);-moz-box-shadow:inset 0 3px 8px rgba(0, 0, 0, 0.125);box-shadow:inset 0 3px 8px rgba(0, 0, 0, 0.125);}
.navbar .btn-navbar{display:none;float:right;padding:7px 10px;margin-left:5px;margin-right:5px;color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#212121;background-image:-moz-linear-gradient(top, #262626, #1a1a1a);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#262626), to(#1a1a1a));background-image:-webkit-linear-gradient(top, #262626, #1a1a1a);background-image:-o-linear-gradient(top, #262626, #1a1a1a);background-image:linear-gradient(to bottom, #262626, #1a1a1a);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff262626', endColorstr='#ff1a1a1a', GradientType=0);border-color:#1a1a1a #1a1a1a #000000;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#1a1a1a;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.075);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.075);}.navbar .btn-navbar:hover,.navbar .btn-navbar:focus,.navbar .btn-navbar:active,.navbar .btn-navbar.active,.navbar .btn-navbar.disabled,.navbar .btn-navbar[disabled]{color:#ffffff;background-color:#1a1a1a;*background-color:#0d0d0d;}
.navbar .btn-navbar:active,.navbar .btn-navbar.active{background-color:#000000 \9;}
.navbar .btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);-moz-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);}
.btn-navbar .icon-bar+.icon-bar{margin-top:3px;}
.navbar .nav>li>.dropdown-menu:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-bottom-color:rgba(0, 0, 0, 0.2);position:absolute;top:-7px;left:9px;}
.navbar .nav>li>.dropdown-menu:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #ffffff;position:absolute;top:-6px;left:10px;}
.navbar-fixed-bottom .nav>li>.dropdown-menu:before{border-top:7px solid #ccc;border-top-color:rgba(0, 0, 0, 0.2);border-bottom:0;bottom:-7px;top:auto;}
.navbar-fixed-bottom .nav>li>.dropdown-menu:after{border-top:6px solid #ffffff;border-bottom:0;bottom:-6px;top:auto;}
.navbar .nav li.dropdown>a:hover .caret,.navbar .nav li.dropdown>a:focus .caret{border-top-color:#aaaaaa;border-bottom-color:#aaaaaa;}
.navbar .nav li.dropdown.open>.dropdown-toggle,.navbar .nav li.dropdown.active>.dropdown-toggle,.navbar .nav li.dropdown.open.active>.dropdown-toggle{background-color:#1a1a1a;color:#aaaaaa;}
.navbar .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;}
.navbar .nav li.dropdown.open>.dropdown-toggle .caret,.navbar .nav li.dropdown.active>.dropdown-toggle .caret,.navbar .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#aaaaaa;border-bottom-color:#aaaaaa;}
.navbar .pull-right>li>.dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right{left:auto;right:0;}.navbar .pull-right>li>.dropdown-menu:before,.navbar .nav>li>.dropdown-menu.pull-right:before{left:auto;right:12px;}
.navbar .pull-right>li>.dropdown-menu:after,.navbar .nav>li>.dropdown-menu.pull-right:after{left:auto;right:13px;}
.navbar .pull-right>li>.dropdown-menu .dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right .dropdown-menu{left:auto;right:100%;margin-left:0;margin-right:-1px;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px;}
.navbar-inverse .navbar-inner{background-color:#1b1b1b;background-image:-moz-linear-gradient(top, #222222, #111111);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#222222), to(#111111));background-image:-webkit-linear-gradient(top, #222222, #111111);background-image:-o-linear-gradient(top, #222222, #111111);background-image:linear-gradient(to bottom, #222222, #111111);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff111111', GradientType=0);border-color:#252525;}
.navbar-inverse .brand,.navbar-inverse .nav>li>a{color:#4d4d4d;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);}.navbar-inverse .brand:hover,.navbar-inverse .nav>li>a:hover,.navbar-inverse .brand:focus,.navbar-inverse .nav>li>a:focus{color:#ffffff;}
.navbar-inverse .brand{color:#4d4d4d;}
.navbar-inverse .navbar-text{color:#4d4d4d;}
.navbar-inverse .nav>li>a:focus,.navbar-inverse .nav>li>a:hover{background-color:transparent;color:#ffffff;}
.navbar-inverse .nav .active>a,.navbar-inverse .nav .active>a:hover,.navbar-inverse .nav .active>a:focus{color:#ffffff;background-color:#111111;}
.navbar-inverse .navbar-link{color:#4d4d4d;}.navbar-inverse .navbar-link:hover,.navbar-inverse .navbar-link:focus{color:#ffffff;}
.navbar-inverse .divider-vertical{border-left-color:#111111;border-right-color:#222222;}
.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{background-color:#111111;color:#ffffff;}
.navbar-inverse .nav li.dropdown>a:hover .caret,.navbar-inverse .nav li.dropdown>a:focus .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;}
.navbar-inverse .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#4d4d4d;border-bottom-color:#4d4d4d;}
.navbar-inverse .nav li.dropdown.open>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;}
.navbar-inverse .navbar-search .search-query{color:#ffffff;background-color:#515151;border-color:#111111;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1), 0 1px 0 rgba(255,255,255,.15);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,.1), 0 1px 0 rgba(255,255,255,.15);box-shadow:inset 0 1px 2px rgba(0,0,0,.1), 0 1px 0 rgba(255,255,255,.15);-webkit-transition:none;-moz-transition:none;-o-transition:none;transition:none;}.navbar-inverse .navbar-search .search-query:-moz-placeholder{color:#cccccc;}
.navbar-inverse .navbar-search .search-query:-ms-input-placeholder{color:#cccccc;}
.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder{color:#cccccc;}
.navbar-inverse .navbar-search .search-query:focus,.navbar-inverse .navbar-search .search-query.focused{padding:5px 15px;color:#333333;text-shadow:0 1px 0 #ffffff;background-color:#ffffff;border:0;-webkit-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);-moz-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);box-shadow:0 0 3px rgba(0, 0, 0, 0.15);outline:0;}
.navbar-inverse .btn-navbar{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#0e0e0e;background-image:-moz-linear-gradient(top, #151515, #040404);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#151515), to(#040404));background-image:-webkit-linear-gradient(top, #151515, #040404);background-image:-o-linear-gradient(top, #151515, #040404);background-image:linear-gradient(to bottom, #151515, #040404);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff151515', endColorstr='#ff040404', GradientType=0);border-color:#040404 #040404 #000000;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#040404;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.navbar-inverse .btn-navbar:hover,.navbar-inverse .btn-navbar:focus,.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active,.navbar-inverse .btn-navbar.disabled,.navbar-inverse .btn-navbar[disabled]{color:#ffffff;background-color:#040404;*background-color:#000000;}
.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active{background-color:#000000 \9;}
.breadcrumb{padding:8px 15px;margin:0 0 20px;list-style:none;background-color:#f5f5f5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.breadcrumb>li{display:inline-block;*display:inline;*zoom:1;text-shadow:0 1px 0 #ffffff;}.breadcrumb>li>.divider{padding:0 5px;color:#ccc;}
.breadcrumb>.active{color:#4d4d4d;}
.pagination{margin:20px 0;}
.pagination ul{display:inline-block;*display:inline;*zoom:1;margin-left:0;margin-bottom:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);}
.pagination ul>li{display:inline;}
.pagination ul>li>a,.pagination ul>li>span{float:left;padding:4px 12px;line-height:20px;text-decoration:none;background-color:#ffffff;border:1px solid #dddddd;border-left-width:0;}
.pagination ul>li>a:hover,.pagination ul>li>a:focus,.pagination ul>.active>a,.pagination ul>.active>span{background-color:#f5f5f5;}
.pagination ul>.active>a,.pagination ul>.active>span{color:#4d4d4d;cursor:default;}
.pagination ul>.disabled>span,.pagination ul>.disabled>a,.pagination ul>.disabled>a:hover,.pagination ul>.disabled>a:focus{color:#4d4d4d;background-color:transparent;cursor:default;}
.pagination ul>li:first-child>a,.pagination ul>li:first-child>span{border-left-width:1px;-webkit-border-top-left-radius:4px;-moz-border-radius-topleft:4px;border-top-left-radius:4px;-webkit-border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;border-bottom-left-radius:4px;}
.pagination ul>li:last-child>a,.pagination ul>li:last-child>span{-webkit-border-top-right-radius:4px;-moz-border-radius-topright:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px;border-bottom-right-radius:4px;}
.pagination-centered{text-align:center;}
.pagination-right{text-align:right;}
.pagination-large ul>li>a,.pagination-large ul>li>span{padding:11px 19px;font-size:17.5px;}
.pagination-large ul>li:first-child>a,.pagination-large ul>li:first-child>span{-webkit-border-top-left-radius:6px;-moz-border-radius-topleft:6px;border-top-left-radius:6px;-webkit-border-bottom-left-radius:6px;-moz-border-radius-bottomleft:6px;border-bottom-left-radius:6px;}
.pagination-large ul>li:last-child>a,.pagination-large ul>li:last-child>span{-webkit-border-top-right-radius:6px;-moz-border-radius-topright:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;-moz-border-radius-bottomright:6px;border-bottom-right-radius:6px;}
.pagination-mini ul>li:first-child>a,.pagination-small ul>li:first-child>a,.pagination-mini ul>li:first-child>span,.pagination-small ul>li:first-child>span{-webkit-border-top-left-radius:3px;-moz-border-radius-topleft:3px;border-top-left-radius:3px;-webkit-border-bottom-left-radius:3px;-moz-border-radius-bottomleft:3px;border-bottom-left-radius:3px;}
.pagination-mini ul>li:last-child>a,.pagination-small ul>li:last-child>a,.pagination-mini ul>li:last-child>span,.pagination-small ul>li:last-child>span{-webkit-border-top-right-radius:3px;-moz-border-radius-topright:3px;border-top-right-radius:3px;-webkit-border-bottom-right-radius:3px;-moz-border-radius-bottomright:3px;border-bottom-right-radius:3px;}
.pagination-small ul>li>a,.pagination-small ul>li>span{padding:2px 10px;font-size:11.9px;}
.pagination-mini ul>li>a,.pagination-mini ul>li>span{padding:0 6px;font-size:10.5px;}
.pager{margin:20px 0;list-style:none;text-align:center;*zoom:1;}.pager:before,.pager:after{display:table;content:"";line-height:0;}
.pager:after{clear:both;}
.pager li{display:inline;}
.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px;}
.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#f5f5f5;}
.pager .next>a,.pager .next>span{float:right;}
.pager .previous>a,.pager .previous>span{float:left;}
.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#4d4d4d;background-color:#fff;cursor:default;}
.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000000;}.modal-backdrop.fade{opacity:0;}
.modal-backdrop,.modal-backdrop.fade.in{opacity:0.8;filter:alpha(opacity=80);}
.modal{position:fixed;top:10%;left:50%;z-index:1050;width:560px;margin-left:-280px;background-color:#ffffff;border:1px solid #999;border:1px solid rgba(0, 0, 0, 0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;outline:none;}.modal.fade{-webkit-transition:opacity .3s linear, top .3s ease-out;-moz-transition:opacity .3s linear, top .3s ease-out;-o-transition:opacity .3s linear, top .3s ease-out;transition:opacity .3s linear, top .3s ease-out;top:-25%;}
.modal.fade.in{top:10%;}
.modal-header{padding:9px 15px;border-bottom:1px solid #eee;}.modal-header .close{margin-top:2px;}
.modal-header h3{margin:0;line-height:30px;}
.modal-body{position:relative;overflow-y:auto;max-height:400px;padding:15px;}
.modal-form{margin-bottom:0;}
.modal-footer{padding:14px 15px 15px;margin-bottom:0;text-align:right;background-color:#f5f5f5;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;-webkit-box-shadow:inset 0 1px 0 #ffffff;-moz-box-shadow:inset 0 1px 0 #ffffff;box-shadow:inset 0 1px 0 #ffffff;*zoom:1;}.modal-footer:before,.modal-footer:after{display:table;content:"";line-height:0;}
.modal-footer:after{clear:both;}
.modal-footer .btn+.btn{margin-left:5px;margin-bottom:0;}
.modal-footer .btn-group .btn+.btn{margin-left:-1px;}
.modal-footer .btn-block+.btn-block{margin-left:0;}
.tooltip{position:absolute;z-index:1030;display:block;visibility:visible;font-size:11px;line-height:1.4;opacity:0;filter:alpha(opacity=0);}.tooltip.in{opacity:0.8;filter:alpha(opacity=80);}
.tooltip.top{margin-top:-3px;padding:5px 0;}
.tooltip.right{margin-left:3px;padding:0 5px;}
.tooltip.bottom{margin-top:3px;padding:5px 0;}
.tooltip.left{margin-left:-3px;padding:0 5px;}
.tooltip-inner{max-width:200px;padding:8px;color:#ffffff;text-align:center;text-decoration:none;background-color:#000000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid;}
.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000000;}
.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000000;}
.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000000;}
.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000000;}
.popover{position:absolute;top:0;left:0;z-index:1010;display:none;max-width:276px;padding:1px;text-align:left;background-color:#ffffff;-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.2);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);white-space:normal;}.popover.top{margin-top:-10px;}
.popover.right{margin-left:10px;}
.popover.bottom{margin-top:10px;}
.popover.left{margin-left:-10px;}
.popover-title{margin:0;padding:8px 14px;font-size:14px;font-weight:normal;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0;}.popover-title:empty{display:none;}
.popover-content{padding:9px 14px;}
.popover .arrow,.popover .arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid;}
.popover .arrow{border-width:11px;}
.popover .arrow:after{border-width:10px;content:"";}
.popover.top .arrow{left:50%;margin-left:-11px;border-bottom-width:0;border-top-color:#999;border-top-color:rgba(0, 0, 0, 0.25);bottom:-11px;}.popover.top .arrow:after{bottom:1px;margin-left:-10px;border-bottom-width:0;border-top-color:#ffffff;}
.popover.right .arrow{top:50%;left:-11px;margin-top:-11px;border-left-width:0;border-right-color:#999;border-right-color:rgba(0, 0, 0, 0.25);}.popover.right .arrow:after{left:1px;bottom:-10px;border-left-width:0;border-right-color:#ffffff;}
.popover.bottom .arrow{left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0, 0, 0, 0.25);top:-11px;}.popover.bottom .arrow:after{top:1px;margin-left:-10px;border-top-width:0;border-bottom-color:#ffffff;}
.popover.left .arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0, 0, 0, 0.25);}.popover.left .arrow:after{right:1px;border-right-width:0;border-left-color:#ffffff;bottom:-10px;}
.thumbnails{margin-left:-20px;list-style:none;*zoom:1;}.thumbnails:before,.thumbnails:after{display:table;content:"";line-height:0;}
.thumbnails:after{clear:both;}
.row-fluid .thumbnails{margin-left:0;}
.thumbnails>li{float:left;margin-bottom:20px;margin-left:20px;}
.thumbnail{display:block;padding:4px;line-height:20px;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.055);-moz-box-shadow:0 1px 3px rgba(0, 0, 0, 0.055);box-shadow:0 1px 3px rgba(0, 0, 0, 0.055);-webkit-transition:all 0.2s ease-in-out;-moz-transition:all 0.2s ease-in-out;-o-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out;}
a.thumbnail:hover,a.thumbnail:focus{border-color:#e66700;-webkit-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);-moz-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);}
.thumbnail>img{display:block;max-width:100%;margin-left:auto;margin-right:auto;}
.thumbnail .caption{padding:9px;color:#333333;}
.media,.media-body{overflow:hidden;*overflow:visible;zoom:1;}
.media,.media .media{margin-top:15px;}
.media:first-child{margin-top:0;}
.media-object{display:block;}
.media-heading{margin:0 0 5px;}
.media>.pull-left{margin-right:10px;}
.media>.pull-right{margin-left:10px;}
.media-list{margin-left:0;list-style:none;}
.label,.badge{display:inline-block;padding:2px 4px;font-size:11.844px;font-weight:bold;line-height:14px;color:#ffffff;vertical-align:baseline;white-space:nowrap;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#4d4d4d;}
.label{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
.badge{padding-left:9px;padding-right:9px;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px;}
.label:empty,.badge:empty{display:none;}
a.label:hover,a.label:focus,a.badge:hover,a.badge:focus{color:#ffffff;text-decoration:none;cursor:pointer;}
.label-important,.badge-important{background-color:#b94a48;}
.label-important[href],.badge-important[href]{background-color:#953b39;}
.label-warning,.badge-warning{background-color:#f89406;}
.label-warning[href],.badge-warning[href]{background-color:#c67605;}
.label-success,.badge-success{background-color:#468847;}
.label-success[href],.badge-success[href]{background-color:#356635;}
.label-info,.badge-info{background-color:#3a87ad;}
.label-info[href],.badge-info[href]{background-color:#2d6987;}
.label-inverse,.badge-inverse{background-color:#333333;}
.label-inverse[href],.badge-inverse[href]{background-color:#1a1a1a;}
.btn .label,.btn .badge{position:relative;top:-1px;}
.btn-mini .label,.btn-mini .badge{top:0;}
@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0;} to{background-position:0 0;}}@-moz-keyframes progress-bar-stripes{from{background-position:40px 0;} to{background-position:0 0;}}@-ms-keyframes progress-bar-stripes{from{background-position:40px 0;} to{background-position:0 0;}}@-o-keyframes progress-bar-stripes{from{background-position:0 0;} to{background-position:40px 0;}}@keyframes progress-bar-stripes{from{background-position:40px 0;} to{background-position:0 0;}}.progress{overflow:hidden;height:20px;margin-bottom:20px;background-color:#f7f7f7;background-image:-moz-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9));background-image:-webkit-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-o-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:linear-gradient(to bottom, #f5f5f5, #f9f9f9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
.progress .bar{width:0%;height:100%;color:#ffffff;float:left;font-size:12px;text-align:center;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#0e90d2;background-image:-moz-linear-gradient(top, #149bdf, #0480be);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be));background-image:-webkit-linear-gradient(top, #149bdf, #0480be);background-image:-o-linear-gradient(top, #149bdf, #0480be);background-image:linear-gradient(to bottom, #149bdf, #0480be);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-transition:width 0.6s ease;-moz-transition:width 0.6s ease;-o-transition:width 0.6s ease;transition:width 0.6s ease;}
.progress .bar+.bar{-webkit-box-shadow:inset 1px 0 0 rgba(0,0,0,.15), inset 0 -1px 0 rgba(0,0,0,.15);-moz-box-shadow:inset 1px 0 0 rgba(0,0,0,.15), inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 1px 0 0 rgba(0,0,0,.15), inset 0 -1px 0 rgba(0,0,0,.15);}
.progress-striped .bar{background-color:#149bdf;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);-webkit-background-size:40px 40px;-moz-background-size:40px 40px;-o-background-size:40px 40px;background-size:40px 40px;}
.progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;-ms-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite;}
.progress-danger .bar,.progress .bar-danger{background-color:#dd514c;background-image:-moz-linear-gradient(top, #ee5f5b, #c43c35);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35));background-image:-webkit-linear-gradient(top, #ee5f5b, #c43c35);background-image:-o-linear-gradient(top, #ee5f5b, #c43c35);background-image:linear-gradient(to bottom, #ee5f5b, #c43c35);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffc43c35', GradientType=0);}
.progress-danger.progress-striped .bar,.progress-striped .bar-danger{background-color:#ee5f5b;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
.progress-success .bar,.progress .bar-success{background-color:#5eb95e;background-image:-moz-linear-gradient(top, #62c462, #57a957);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957));background-image:-webkit-linear-gradient(top, #62c462, #57a957);background-image:-o-linear-gradient(top, #62c462, #57a957);background-image:linear-gradient(to bottom, #62c462, #57a957);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff57a957', GradientType=0);}
.progress-success.progress-striped .bar,.progress-striped .bar-success{background-color:#62c462;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
.progress-info .bar,.progress .bar-info{background-color:#4bb1cf;background-image:-moz-linear-gradient(top, #5bc0de, #339bb9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9));background-image:-webkit-linear-gradient(top, #5bc0de, #339bb9);background-image:-o-linear-gradient(top, #5bc0de, #339bb9);background-image:linear-gradient(to bottom, #5bc0de, #339bb9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff339bb9', GradientType=0);}
.progress-info.progress-striped .bar,.progress-striped .bar-info{background-color:#5bc0de;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
.progress-warning .bar,.progress .bar-warning{background-color:#faa732;background-image:-moz-linear-gradient(top, #fbb450, #f89406);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));background-image:-webkit-linear-gradient(top, #fbb450, #f89406);background-image:-o-linear-gradient(top, #fbb450, #f89406);background-image:linear-gradient(to bottom, #fbb450, #f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0);}
.progress-warning.progress-striped .bar,.progress-striped .bar-warning{background-color:#fbb450;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
.accordion{margin-bottom:20px;}
.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
.accordion-heading{border-bottom:0;}
.accordion-heading .accordion-toggle{display:block;padding:8px 15px;}
.accordion-toggle{cursor:pointer;}
.accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5;}
.carousel{position:relative;margin-bottom:20px;line-height:1;}
.carousel-inner{overflow:hidden;width:100%;position:relative;}
.carousel-inner>.item{display:none;position:relative;-webkit-transition:0.6s ease-in-out left;-moz-transition:0.6s ease-in-out left;-o-transition:0.6s ease-in-out left;transition:0.6s ease-in-out left;}.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;line-height:1;}
.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block;}
.carousel-inner>.active{left:0;}
.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%;}
.carousel-inner>.next{left:100%;}
.carousel-inner>.prev{left:-100%;}
.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0;}
.carousel-inner>.active.left{left:-100%;}
.carousel-inner>.active.right{left:100%;}
.carousel-control{position:absolute;top:40%;left:15px;width:40px;height:40px;margin-top:-20px;font-size:60px;font-weight:100;line-height:30px;color:#ffffff;text-align:center;background:#1a1a1a;border:3px solid #ffffff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:0.5;filter:alpha(opacity=50);}.carousel-control.right{left:auto;right:15px;}
.carousel-control:hover,.carousel-control:focus{color:#ffffff;text-decoration:none;opacity:0.9;filter:alpha(opacity=90);}
.carousel-indicators{position:absolute;top:15px;right:15px;z-index:5;margin:0;list-style:none;}.carousel-indicators li{display:block;float:left;width:10px;height:10px;margin-left:5px;text-indent:-999px;background-color:#ccc;background-color:rgba(255, 255, 255, 0.25);border-radius:5px;}
.carousel-indicators .active{background-color:#fff;}
.carousel-caption{position:absolute;left:0;right:0;bottom:0;padding:15px;background:#333333;background:rgba(0, 0, 0, 0.75);}
.carousel-caption h4,.carousel-caption p{color:#ffffff;line-height:20px;}
.carousel-caption h4{margin:0 0 5px;}
.carousel-caption p{margin-bottom:0;}
.hero-unit{padding:60px;margin-bottom:30px;font-size:18px;font-weight:200;line-height:30px;color:inherit;background-color:#eeeeee;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;color:inherit;letter-spacing:-1px;}
.hero-unit li{line-height:30px;}
.pull-right{float:right;}
.pull-left{float:left;}
.hide{display:none;}
.show{display:block;}
.invisible{visibility:hidden;}
.affix{position:fixed;}

File diff suppressed because one or more lines are too long

View File

@ -1,6 +0,0 @@
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}

View File

@ -1,39 +0,0 @@
.jvectormap-label {
position: absolute;
display: none;
border: solid 1px #CDCDCD;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
background: #292929;
color: white;
font-family: sans-serif, Verdana;
font-size: smaller;
padding: 3px;
}
.jvectormap-zoomin, .jvectormap-zoomout {
position: absolute;
left: 10px;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
background: #292929;
padding: 3px;
color: white;
width: 10px;
height: 10px;
cursor: pointer;
line-height: 10px;
text-align: center;
}
.jvectormap-zoomin {
display: none;
top: 10px;
}
.jvectormap-zoomout {
display: none;
top: 30px;
}

View File

@ -6,6 +6,12 @@
color: #000;
}
.spy {
position:absolute;
right:0px;
top:0px;
}
.navbar .brand {
color: #eee;
}
@ -101,6 +107,10 @@
cursor: pointer;
}
.pointer:hover {
color: #fff;
}
.pointer {
cursor: pointer;
}
@ -113,8 +123,6 @@
max-width: 500px;
}
.popover-title { display: none; }
.tiny {
font-size: 50%;
}

View File

@ -91,7 +91,7 @@ function top_field_values(docs,field,count) {
}
function add_to_query(original,field,value,negate) {
var not = negate ? "NOT " : "";
var not = negate ? "-" : "";
if(value !== '')
var query = field + ":" + "\"" + addslashes(value.toString()) + "\"";
else
@ -284,7 +284,7 @@ function flatten_json(object,root,array) {
} else if(obj.length === 1 && _.isNumber(obj[0])) {
array[rootname] = parseFloat(obj[0]);
} else {
array[rootname] = typeof obj === 'undefined' ? null : obj.join(',');
array[rootname] = typeof obj === 'undefined' ? null : obj;
}
} else {
flatten_json(obj,rootname,array)

View File

@ -1,32 +0,0 @@
// Underscore.js 1.3.3
// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
// Underscore is freely distributable under the MIT license.
// Portions of Underscore are inspired or borrowed from Prototype,
// Oliver Steele's Functional, and John Resig's Micro-Templating.
// For all details and documentation:
// http://documentcloud.github.com/underscore
(function(){function r(a,c,d){if(a===c)return 0!==a||1/a==1/c;if(null==a||null==c)return a===c;a._chain&&(a=a._wrapped);c._chain&&(c=c._wrapped);if(a.isEqual&&b.isFunction(a.isEqual))return a.isEqual(c);if(c.isEqual&&b.isFunction(c.isEqual))return c.isEqual(a);var e=l.call(a);if(e!=l.call(c))return!1;switch(e){case "[object String]":return a==""+c;case "[object Number]":return a!=+a?c!=+c:0==a?1/a==1/c:a==+c;case "[object Date]":case "[object Boolean]":return+a==+c;case "[object RegExp]":return a.source==
c.source&&a.global==c.global&&a.multiline==c.multiline&&a.ignoreCase==c.ignoreCase}if("object"!=typeof a||"object"!=typeof c)return!1;for(var f=d.length;f--;)if(d[f]==a)return!0;d.push(a);var f=0,g=!0;if("[object Array]"==e){if(f=a.length,g=f==c.length)for(;f--&&(g=f in a==f in c&&r(a[f],c[f],d)););}else{if("constructor"in a!="constructor"in c||a.constructor!=c.constructor)return!1;for(var h in a)if(b.has(a,h)&&(f++,!(g=b.has(c,h)&&r(a[h],c[h],d))))break;if(g){for(h in c)if(b.has(c,h)&&!f--)break;
g=!f}}d.pop();return g}var s=this,I=s._,o={},k=Array.prototype,p=Object.prototype,i=k.slice,J=k.unshift,l=p.toString,K=p.hasOwnProperty,y=k.forEach,z=k.map,A=k.reduce,B=k.reduceRight,C=k.filter,D=k.every,E=k.some,q=k.indexOf,F=k.lastIndexOf,p=Array.isArray,L=Object.keys,t=Function.prototype.bind,b=function(a){return new m(a)};"undefined"!==typeof exports?("undefined"!==typeof module&&module.exports&&(exports=module.exports=b),exports._=b):s._=b;b.VERSION="1.3.3";var j=b.each=b.forEach=function(a,
c,d){if(a!=null)if(y&&a.forEach===y)a.forEach(c,d);else if(a.length===+a.length)for(var e=0,f=a.length;e<f;e++){if(e in a&&c.call(d,a[e],e,a)===o)break}else for(e in a)if(b.has(a,e)&&c.call(d,a[e],e,a)===o)break};b.map=b.collect=function(a,c,b){var e=[];if(a==null)return e;if(z&&a.map===z)return a.map(c,b);j(a,function(a,g,h){e[e.length]=c.call(b,a,g,h)});if(a.length===+a.length)e.length=a.length;return e};b.reduce=b.foldl=b.inject=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(A&&
a.reduce===A){e&&(c=b.bind(c,e));return f?a.reduce(c,d):a.reduce(c)}j(a,function(a,b,i){if(f)d=c.call(e,d,a,b,i);else{d=a;f=true}});if(!f)throw new TypeError("Reduce of empty array with no initial value");return d};b.reduceRight=b.foldr=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(B&&a.reduceRight===B){e&&(c=b.bind(c,e));return f?a.reduceRight(c,d):a.reduceRight(c)}var g=b.toArray(a).reverse();e&&!f&&(c=b.bind(c,e));return f?b.reduce(g,c,d,e):b.reduce(g,c)};b.find=b.detect=function(a,
c,b){var e;G(a,function(a,g,h){if(c.call(b,a,g,h)){e=a;return true}});return e};b.filter=b.select=function(a,c,b){var e=[];if(a==null)return e;if(C&&a.filter===C)return a.filter(c,b);j(a,function(a,g,h){c.call(b,a,g,h)&&(e[e.length]=a)});return e};b.reject=function(a,c,b){var e=[];if(a==null)return e;j(a,function(a,g,h){c.call(b,a,g,h)||(e[e.length]=a)});return e};b.every=b.all=function(a,c,b){var e=true;if(a==null)return e;if(D&&a.every===D)return a.every(c,b);j(a,function(a,g,h){if(!(e=e&&c.call(b,
a,g,h)))return o});return!!e};var G=b.some=b.any=function(a,c,d){c||(c=b.identity);var e=false;if(a==null)return e;if(E&&a.some===E)return a.some(c,d);j(a,function(a,b,h){if(e||(e=c.call(d,a,b,h)))return o});return!!e};b.include=b.contains=function(a,c){var b=false;if(a==null)return b;if(q&&a.indexOf===q)return a.indexOf(c)!=-1;return b=G(a,function(a){return a===c})};b.invoke=function(a,c){var d=i.call(arguments,2);return b.map(a,function(a){return(b.isFunction(c)?c||a:a[c]).apply(a,d)})};b.pluck=
function(a,c){return b.map(a,function(a){return a[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a)&&a[0]===+a[0])return Math.max.apply(Math,a);if(!c&&b.isEmpty(a))return-Infinity;var e={computed:-Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b>=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a)&&a[0]===+a[0])return Math.min.apply(Math,a);if(!c&&b.isEmpty(a))return Infinity;var e={computed:Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b<e.computed&&
(e={value:a,computed:b})});return e.value};b.shuffle=function(a){var b=[],d;j(a,function(a,f){d=Math.floor(Math.random()*(f+1));b[f]=b[d];b[d]=a});return b};b.sortBy=function(a,c,d){var e=b.isFunction(c)?c:function(a){return a[c]};return b.pluck(b.map(a,function(a,b,c){return{value:a,criteria:e.call(d,a,b,c)}}).sort(function(a,b){var c=a.criteria,d=b.criteria;return c===void 0?1:d===void 0?-1:c<d?-1:c>d?1:0}),"value")};b.groupBy=function(a,c){var d={},e=b.isFunction(c)?c:function(a){return a[c]};
j(a,function(a,b){var c=e(a,b);(d[c]||(d[c]=[])).push(a)});return d};b.sortedIndex=function(a,c,d){d||(d=b.identity);for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(c)?e=g+1:f=g}return e};b.toArray=function(a){return!a?[]:b.isArray(a)||b.isArguments(a)?i.call(a):a.toArray&&b.isFunction(a.toArray)?a.toArray():b.values(a)};b.size=function(a){return b.isArray(a)?a.length:b.keys(a).length};b.first=b.head=b.take=function(a,b,d){return b!=null&&!d?i.call(a,0,b):a[0]};b.initial=function(a,b,d){return i.call(a,
0,a.length-(b==null||d?1:b))};b.last=function(a,b,d){return b!=null&&!d?i.call(a,Math.max(a.length-b,0)):a[a.length-1]};b.rest=b.tail=function(a,b,d){return i.call(a,b==null||d?1:b)};b.compact=function(a){return b.filter(a,function(a){return!!a})};b.flatten=function(a,c){return b.reduce(a,function(a,e){if(b.isArray(e))return a.concat(c?e:b.flatten(e));a[a.length]=e;return a},[])};b.without=function(a){return b.difference(a,i.call(arguments,1))};b.uniq=b.unique=function(a,c,d){var d=d?b.map(a,d):a,
e=[];a.length<3&&(c=true);b.reduce(d,function(d,g,h){if(c?b.last(d)!==g||!d.length:!b.include(d,g)){d.push(g);e.push(a[h])}return d},[]);return e};b.union=function(){return b.uniq(b.flatten(arguments,true))};b.intersection=b.intersect=function(a){var c=i.call(arguments,1);return b.filter(b.uniq(a),function(a){return b.every(c,function(c){return b.indexOf(c,a)>=0})})};b.difference=function(a){var c=b.flatten(i.call(arguments,1),true);return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a=
i.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e<c;e++)d[e]=b.pluck(a,""+e);return d};b.indexOf=function(a,c,d){if(a==null)return-1;var e;if(d){d=b.sortedIndex(a,c);return a[d]===c?d:-1}if(q&&a.indexOf===q)return a.indexOf(c);d=0;for(e=a.length;d<e;d++)if(d in a&&a[d]===c)return d;return-1};b.lastIndexOf=function(a,b){if(a==null)return-1;if(F&&a.lastIndexOf===F)return a.lastIndexOf(b);for(var d=a.length;d--;)if(d in a&&a[d]===b)return d;return-1};b.range=function(a,b,d){if(arguments.length<=
1){b=a||0;a=0}for(var d=arguments[2]||1,e=Math.max(Math.ceil((b-a)/d),0),f=0,g=Array(e);f<e;){g[f++]=a;a=a+d}return g};var H=function(){};b.bind=function(a,c){var d,e;if(a.bind===t&&t)return t.apply(a,i.call(arguments,1));if(!b.isFunction(a))throw new TypeError;e=i.call(arguments,2);return d=function(){if(!(this instanceof d))return a.apply(c,e.concat(i.call(arguments)));H.prototype=a.prototype;var b=new H,g=a.apply(b,e.concat(i.call(arguments)));return Object(g)===g?g:b}};b.bindAll=function(a){var c=
i.call(arguments,1);c.length==0&&(c=b.functions(a));j(c,function(c){a[c]=b.bind(a[c],a)});return a};b.memoize=function(a,c){var d={};c||(c=b.identity);return function(){var e=c.apply(this,arguments);return b.has(d,e)?d[e]:d[e]=a.apply(this,arguments)}};b.delay=function(a,b){var d=i.call(arguments,2);return setTimeout(function(){return a.apply(null,d)},b)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(i.call(arguments,1)))};b.throttle=function(a,c){var d,e,f,g,h,i,j=b.debounce(function(){h=
g=false},c);return function(){d=this;e=arguments;f||(f=setTimeout(function(){f=null;h&&a.apply(d,e);j()},c));g?h=true:i=a.apply(d,e);j();g=true;return i}};b.debounce=function(a,b,d){var e;return function(){var f=this,g=arguments;d&&!e&&a.apply(f,g);clearTimeout(e);e=setTimeout(function(){e=null;d||a.apply(f,g)},b)}};b.once=function(a){var b=false,d;return function(){if(b)return d;b=true;return d=a.apply(this,arguments)}};b.wrap=function(a,b){return function(){var d=[a].concat(i.call(arguments,0));
return b.apply(this,d)}};b.compose=function(){var a=arguments;return function(){for(var b=arguments,d=a.length-1;d>=0;d--)b=[a[d].apply(this,b)];return b[0]}};b.after=function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};b.keys=L||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var c=[],d;for(d in a)b.has(a,d)&&(c[c.length]=d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&&
c.push(d);return c.sort()};b.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]=b[d]});return a};b.pick=function(a){var c={};j(b.flatten(i.call(arguments,1)),function(b){b in a&&(c[b]=a[b])});return c};b.defaults=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return!b.isObject(a)?a:b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,b){return r(a,b,[])};b.isEmpty=
function(a){if(a==null)return true;if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(b.has(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=p||function(a){return l.call(a)=="[object Array]"};b.isObject=function(a){return a===Object(a)};b.isArguments=function(a){return l.call(a)=="[object Arguments]"};b.isArguments(arguments)||(b.isArguments=function(a){return!(!a||!b.has(a,"callee"))});b.isFunction=function(a){return l.call(a)=="[object Function]"};
b.isString=function(a){return l.call(a)=="[object String]"};b.isNumber=function(a){return l.call(a)=="[object Number]"};b.isFinite=function(a){return b.isNumber(a)&&isFinite(a)};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===true||a===false||l.call(a)=="[object Boolean]"};b.isDate=function(a){return l.call(a)=="[object Date]"};b.isRegExp=function(a){return l.call(a)=="[object RegExp]"};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.has=function(a,
b){return K.call(a,b)};b.noConflict=function(){s._=I;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e<a;e++)b.call(d,e)};b.escape=function(a){return(""+a).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#x27;").replace(/\//g,"&#x2F;")};b.result=function(a,c){if(a==null)return null;var d=a[c];return b.isFunction(d)?d.call(a):d};b.mixin=function(a){j(b.functions(a),function(c){M(c,b[c]=a[c])})};var N=0;b.uniqueId=
function(a){var b=N++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var u=/.^/,n={"\\":"\\","'":"'",r:"\r",n:"\n",t:"\t",u2028:"\u2028",u2029:"\u2029"},v;for(v in n)n[n[v]]=v;var O=/\\|'|\r|\n|\t|\u2028|\u2029/g,P=/\\(\\|'|r|n|t|u2028|u2029)/g,w=function(a){return a.replace(P,function(a,b){return n[b]})};b.template=function(a,c,d){d=b.defaults(d||{},b.templateSettings);a="__p+='"+a.replace(O,function(a){return"\\"+n[a]}).replace(d.escape||
u,function(a,b){return"'+\n_.escape("+w(b)+")+\n'"}).replace(d.interpolate||u,function(a,b){return"'+\n("+w(b)+")+\n'"}).replace(d.evaluate||u,function(a,b){return"';\n"+w(b)+"\n;__p+='"})+"';\n";d.variable||(a="with(obj||{}){\n"+a+"}\n");var a="var __p='';var print=function(){__p+=Array.prototype.join.call(arguments, '')};\n"+a+"return __p;\n",e=new Function(d.variable||"obj","_",a);if(c)return e(c,b);c=function(a){return e.call(this,a,b)};c.source="function("+(d.variable||"obj")+"){\n"+a+"}";return c};
b.chain=function(a){return b(a).chain()};var m=function(a){this._wrapped=a};b.prototype=m.prototype;var x=function(a,c){return c?b(a).chain():a},M=function(a,c){m.prototype[a]=function(){var a=i.call(arguments);J.call(a,this._wrapped);return x(c.apply(b,a),this._chain)}};b.mixin(b);j("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){var b=k[a];m.prototype[a]=function(){var d=this._wrapped;b.apply(d,arguments);var e=d.length;(a=="shift"||a=="splice")&&e===0&&delete d[0];return x(d,
this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];m.prototype[a]=function(){return x(b.apply(this._wrapped,arguments),this._chain)}});m.prototype.chain=function(){this._chain=true;return this};m.prototype.value=function(){return this._wrapped}}).call(this);

View File

@ -20,8 +20,8 @@ var config = new Settings(
elasticsearch: "http://"+window.location.hostname+":9200",
// elasticsearch: 'http://localhost:9200',
kibana_index: "kibana-int",
modules: ['histogram','map','pie','table','stringquery','sort',
modules: ['histogram','map','pie','table','filtering',
'timepicker','text','fields','hits','dashcontrol',
'column','derivequeries','trends','bettermap'],
'column','derivequeries','trends','bettermap','query'],
}
);

View File

@ -1,5 +1,47 @@
{
"title": "Logstash Search",
"services": {
"query": {
"idQueue": [
1,
2,
3,
4
],
"list": {
"0": {
"query": "*",
"alias": "",
"color": "#7EB26D",
"id": 0
}
},
"ids": [
0
]
},
"filter": {
"idQueue": [
1,
2
],
"list": {
"0": {
"from": "2013-07-15T16:50:45.363Z",
"to": "2013-07-15T17:50:45.363Z",
"field": "@timestamp",
"type": "time",
"mandate": "must",
"active": true,
"alias": "",
"id": 0
}
},
"ids": [
0
]
}
},
"rows": [
{
"title": "Options",
@ -10,13 +52,14 @@
"panels": [
{
"loading": false,
"error": false,
"error": "",
"span": 5,
"editable": true,
"group": [
"default"
],
"type": "timepicker",
"status": "Stable",
"mode": "relative",
"time_options": [
"5m",
@ -26,18 +69,18 @@
"12h",
"24h",
"2d",
"5d"
"7d",
"30d"
],
"timespan": "6h",
"timespan": "1h",
"timefield": "@timestamp",
"index": "[logstash-]YYYY.MM.DD",
"defaultindex": "NOINDEX",
"index_interval": "day",
"timeformat": "",
"refresh": {
"enable": false,
"interval": 30,
"min": 3
}
},
"filter_id": 0
},
{
"loading": false,
@ -48,6 +91,7 @@
"default"
],
"type": "dashcontrol",
"status": "Stable",
"save": {
"gist": false,
"elasticsearch": true,
@ -61,7 +105,6 @@
},
"hide_control": false,
"elasticsearch_size": 20,
"elasticsearch_saveto": "kibana-int",
"temp": true,
"temp_ttl": "30d"
}
@ -82,16 +125,32 @@
"group": [
"default"
],
"type": "stringquery",
"type": "query",
"status": "Experimental",
"label": "Search",
"query": "*",
"size": 100,
"sort": [
"_score",
"desc"
"history": [],
"remember": 10
}
]
},
{
"title": "Filters",
"height": "50px",
"editable": true,
"collapse": true,
"collapsable": true,
"panels": [
{
"loading": false,
"error": false,
"span": 12,
"editable": true,
"group": [
"default"
],
"multi": false,
"multi_arrange": "horizontal"
"type": "filtering",
"status": "Experimental"
}
]
},
@ -110,55 +169,33 @@
"default"
],
"type": "histogram",
"status": "Stable",
"query": [
{
"query": "*",
"label": "*"
"label": "Query"
}
],
"interval": "5m",
"show": [
"points",
"lines",
"legend",
"x-axis",
"y-axis"
],
"mode": "count",
"time_field": "@timestamp",
"value_field": null,
"auto_int": true,
"resolution": 100,
"interval": "30s",
"fill": 3,
"linewidth": 3,
"timezone": "browser",
"spyable": true,
"zoomlinks": true,
"fill": 0,
"linewidth": 2,
"bars": true,
"stack": true,
"stack": false,
"points": false,
"lines": false,
"legend": true,
"x-axis": true,
"y-axis": true
},
{
"loading": false,
"span": 0,
"editable": true,
"group": [
"default"
],
"type": "hits",
"query": [
{
"query": "*",
"label": "*"
}
],
"style": {
"font-size": "9pt"
},
"aggregate": false,
"arrangement": "horizontal",
"chart": true,
"counters": true,
"count_pos": "above"
"y-axis": true,
"percentage": false,
"interactive": true
}
]
},
@ -178,6 +215,7 @@
"default"
],
"type": "fields",
"status": "Beta",
"style": {},
"arrange": "vertical",
"micropanel_position": "right",
@ -196,21 +234,10 @@
"default"
],
"type": "table",
"status": "Stable",
"query": "*",
"interval": "1y",
"show": [
"bars",
"y-axis",
"x-axis",
"legend"
],
"fill": 3,
"overflow": "min-height",
"timezone": "browser",
"spyable": true,
"zoomlinks": true,
"size": 50,
"pages": 10,
"size": 100,
"pages": 5,
"offset": 0,
"sort": [
"@timestamp",
@ -219,14 +246,24 @@
"style": {
"font-size": "9pt"
},
"overflow": "min-height",
"fields": [
"@timestamp",
"@message"
],
"sortable": true
"highlight": [],
"sortable": true,
"header": true,
"paging": true,
"spyable": true
}
]
}
],
"editable": true
"editable": true,
"index": {
"interval": "day",
"pattern": "[logstash-]YYYY.MM.DD",
"default": "logstash-*"
}
}

View File

@ -6,18 +6,17 @@
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="description" content="Search based application built using ElasticSearch, elastic.js, and Angular.js">
<meta name="viewport" content="width=device-width">
<title>Kibana 3</title>
<link rel="stylesheet" href="common/css/normalize.min.css">
<link rel="stylesheet" href="common/css/bootstrap.dark.min.css">
<link rel="stylesheet" href="common/css/bootstrap.dark.min.css" title="Dark">
<link rel="alternate stylesheet" href="common/css/bootstrap.light.min.css" title="Light">
<link rel="stylesheet" href="common/css/animate.min.css">
<link rel="stylesheet" href="common/css/bootstrap-responsive.min.css">
<link rel="stylesheet" href="common/css/font-awesome.min.css">
<link rel="stylesheet" href="common/css/main.css">
<link rel="stylesheet" href="common/css/elasticjs.css">
<link rel="stylesheet" href="common/css/timepicker.css">
<!-- project dependency libs -->
@ -38,9 +37,9 @@
<div class="navbar navbar-static-top">
<div class="navbar-inner">
<div class="container-fluid">
<p class="navbar-text pull-right"><small><strong>Kibana 3</strong> <small>milestone 2</small></small></p>
<span class="brand">{{dashboards.title}}</span>
<div class="brand"><i class='icon-cog pointer' ng-show='dashboards.editable' bs-modal="'partials/dasheditor.html'"></i></div>
<p class="navbar-text pull-right"><small><strong>Kibana 3</strong> <small>milestone pre-3</small></small></p>
<span class="brand">{{dashboard.current.title}}</span>
<div class="brand"><i class='icon-cog pointer' ng-show='dashboard.current.editable' bs-modal="'partials/dasheditor.html'"></i></div>
</div>
</div>
</div>

View File

@ -19,7 +19,6 @@ var scripts = []
var labjs = $LAB
.script("common/lib/jquery-1.8.0.min.js").wait()
.script("common/lib/modernizr-2.6.1.min.js")
.script("common/lib/underscore.min.js")
.script("common/lib/angular.min.js").wait()
.script("common/lib/angular-strap.min.js")
.script("common/lib/angular-sanitize.min.js")
@ -47,10 +46,10 @@ labjs.wait(function(){
angular.module('kibana', modules).config(['$routeProvider', function($routeProvider) {
$routeProvider
.when('/dashboard', {
templateUrl: 'partials/dashboard.html'
templateUrl: 'partials/dashboard.html',
})
.when('/dashboard/:type/:id', {
templateUrl: 'partials/dashboard.html'
templateUrl: 'partials/dashboard.html',
})
.when('/dashboard/:type/:id/:params', {
templateUrl: 'partials/dashboard.html'

View File

@ -3,7 +3,8 @@
'use strict';
angular.module('kibana.controllers', [])
.controller('DashCtrl', function($scope, $rootScope, $http, $timeout, ejsResource, eventBus, fields) {
.controller('DashCtrl', function($scope, $rootScope, $http, $timeout, $route, ejsResource, eventBus,
fields, dashboard) {
var _d = {
title: "",
@ -18,28 +19,18 @@ angular.module('kibana.controllers', [])
// Make underscore.js available to views
$scope._ = _;
$scope.dashboard = dashboard;
// Provide a global list of all see fields
$scope.fields = fields
$scope.reset_row();
$scope.clear_all_alerts();
// Load dashboard by event
eventBus.register($scope,'dashboard', function(event,dashboard){
$scope.dashboards = dashboard.dashboard;
$scope.dashboards.last = dashboard.last;
_.defaults($scope.dashboards,_d)
})
// If the route changes, clear the existing dashboard
$rootScope.$on( "$routeChangeStart", function(event, next, current) {
delete $scope.dashboards
});
var ejs = $scope.ejs = ejsResource(config.elasticsearch);
}
$scope.add_row = function(dashboards,row) {
$scope.dashboards.rows.push(row);
$scope.add_row = function(dash,row) {
dash.rows.push(row);
}
$scope.reset_row = function() {

View File

@ -49,39 +49,7 @@ angular.module('kibana.directives', [])
}
};
})
.directive('upload', function(timer){
return {
restrict: 'A',
link: function(scope, elem, attrs) {
function file_selected(evt) {
var files = evt.target.files; // FileList object
// files is a FileList of File objects. List some properties.
var output = [];
for (var i = 0, f; f = files[i]; i++) {
var reader = new FileReader();
reader.onload = (function(theFile) {
return function(e) {
// Render thumbnail.
scope.dashboards = JSON.parse(e.target.result)
timer.cancel_all();
scope.$apply();
};
})(f);
reader.readAsText(f);
}
}
// Check for the various File API support.
if (window.File && window.FileReader && window.FileList && window.Blob) {
// Something
document.getElementById('upload').addEventListener('change', file_selected, false);
} else {
alert('Sorry, the HTML5 File APIs are not fully supported in this browser.');
}
}
}
}).directive('ngModelOnblur', function() {
.directive('ngModelOnblur', function() {
return {
restrict: 'A',
require: 'ngModel',

View File

@ -23,7 +23,6 @@ angular.module('kibana.services', [])
if(_.contains(_types,'$kibana_debug'))
$rootScope.$broadcast('$kibana_debug',packet);
//console.log('Sent: '+type + ' to ' + to + ' from ' + from + ': ' + angular.toJson(data))
$rootScope.$broadcast(type,{
from: from,
to: to,
@ -46,13 +45,12 @@ angular.module('kibana.services', [])
var _time = packet.time
var _group = (!(_.isUndefined(scope.panel))) ? scope.panel.group : ["NONE"]
//console.log('registered:' + type + " for " + scope.panel.title + " " + scope.$id)
if(!(_.isArray(_to)))
_to = [_to];
if(!(_.isArray(_group)))
_group = [_group];
// Transmit even only if the send is not the receiver AND one of the following:
// Transmit event only if the sender is not the receiver AND one of the following:
// 1) Receiver has group in _to 2) Receiver's $id is in _to
// 3) Event is addressed to ALL 4) Receiver is in ALL group
if((_.intersection(_to,_group).length > 0 ||
@ -61,14 +59,13 @@ angular.module('kibana.services', [])
_.indexOf(_to,'ALL') > -1) &&
_from !== _id
) {
//console.log('Got: '+type + ' from ' + _from + ' to ' + _to + ': ' + angular.toJson(packet.data))
fn(event,packet.data,{time:_time,to:_to,from:_from,type:_type});
}
});
}
})
/* Service: fields
/*
Service: fields
Provides a global list of all seen fields for use in editor panels
*/
.factory('fields', function($rootScope) {
@ -84,6 +81,7 @@ angular.module('kibana.services', [])
})
.service('kbnIndex',function($http) {
// returns a promise containing an array of all indices matching the index
// pattern that exist in a given range
this.indices = function(from,to,pattern,interval) {
@ -119,7 +117,7 @@ angular.module('kibana.services', [])
}
// this is stupid, but there is otherwise no good way to ensure that when
// I extract the date from an object that I'm get the UTC date. Stupid js.
// I extract the date from an object that I get the UTC date. Stupid js.
// I die a little inside every time I call this function.
// Update: I just read this again. I died a little more inside.
// Update2: More death.
@ -185,6 +183,581 @@ angular.module('kibana.services', [])
timers = new Array();
}
})
.service('query', function(dashboard) {
// Create an object to hold our service state on the dashboard
dashboard.current.services.query = dashboard.current.services.query || {};
_.defaults(dashboard.current.services.query,{
idQueue : [],
list : {},
ids : [],
});
// For convenience
var _q = dashboard.current.services.query;
this.colors = [
"#7EB26D","#EAB839","#6ED0E0","#EF843C","#E24D42","#1F78C1","#BA43A9","#705DA0", //1
"#508642","#CCA300","#447EBC","#C15C17","#890F02","#0A437C","#6D1F62","#584477", //2
"#B7DBAB","#F4D598","#70DBED","#F9BA8F","#F29191","#82B5D8","#E5A8E2","#AEA2E0", //3
"#629E51","#E5AC0E","#64B0C8","#E0752D","#BF1B00","#0A50A1","#962D82","#614D93", //4
"#9AC48A","#F2C96D","#65C5DB","#F9934E","#EA6460","#5195CE","#D683CE","#806EB7", //5
"#3F6833","#967302","#2F575E","#99440A","#58140C","#052B51","#511749","#3F2B5B", //6
"#E0F9D7","#FCEACA","#CFFAFF","#F9E2D2","#FCE2DE","#BADFF4","#F9D9F9","#DEDAF7" //7
];
// Save a reference to this
var self = this;
this.init = function() {
_q = dashboard.current.services.query;
self.list = dashboard.current.services.query.list;
self.ids = dashboard.current.services.query.ids;
if (self.ids.length == 0) {
self.set({});
}
}
// This is used both for adding queries and modifying them. If an id is passed, the query at that id is updated
this.set = function(query,id) {
if(!_.isUndefined(id)) {
if(!_.isUndefined(self.list[id])) {
_.extend(self.list[id],query);
return id;
} else {
return false;
}
} else {
var _id = nextId();
var _query = {
query: '*',
alias: '',
color: colorAt(_id),
id: _id
}
_.defaults(query,_query)
self.list[_id] = query;
self.ids.push(_id)
return _id;
}
}
this.remove = function(id) {
if(!_.isUndefined(self.list[id])) {
delete self.list[id];
// This must happen on the full path also since _.without returns a copy
self.ids = dashboard.current.services.query.ids = _.without(self.ids,id)
_q.idQueue.unshift(id)
_q.idQueue.sort(function(a,b){return a-b});
return true;
} else {
return false;
}
}
this.findQuery = function(queryString) {
return _.findWhere(self.list,{query:queryString})
}
var nextId = function() {
if(_q.idQueue.length > 0) {
return _q.idQueue.shift()
} else {
return self.ids.length;
}
}
var colorAt = function(id) {
return self.colors[id % self.colors.length]
}
self.init();
})
.service('filterSrv', function(dashboard, ejsResource) {
// Create an object to hold our service state on the dashboard
dashboard.current.services.filter = dashboard.current.services.filter || {};
_.defaults(dashboard.current.services.filter,{
idQueue : [],
list : {},
ids : [],
});
// For convenience
var ejs = ejsResource(config.elasticsearch);
var _f = dashboard.current.services.filter;
// Save a reference to this
var self = this;
// Call this whenever we need to reload the important stuff
this.init = function() {
// Accessors
self.list = dashboard.current.services.filter.list;
self.ids = dashboard.current.services.filter.ids;
_f = dashboard.current.services.filter;
_.each(self.getByType('time',true),function(time) {
self.list[time.id].from = new Date(time.from)
self.list[time.id].to = new Date(time.to)
})
}
// This is used both for adding filters and modifying them.
// If an id is passed, the filter at that id is updated
this.set = function(filter,id) {
_.defaults(filter,{mandate:'must'})
filter.active = true;
if(!_.isUndefined(id)) {
if(!_.isUndefined(self.list[id])) {
_.extend(self.list[id],filter);
return id;
} else {
return false;
}
} else {
if(_.isUndefined(filter.type)) {
return false;
} else {
var _id = nextId();
var _filter = {
alias: '',
id: _id
}
_.defaults(filter,_filter)
self.list[_id] = filter;
self.ids.push(_id)
return _id;
}
}
}
this.getBoolFilter = function(ids) {
// A default match all filter, just in case there are no other filters
var bool = ejs.BoolFilter().must(ejs.MatchAllFilter());
_.each(ids,function(id) {
if(self.list[id].active) {
switch(self.list[id].mandate)
{
case 'mustNot':
bool = bool.mustNot(self.getEjsObj(id));
break;
case 'should':
bool = bool.should(self.getEjsObj(id));
break;
default:
bool = bool.must(self.getEjsObj(id));
}
}
})
return bool;
}
this.getEjsObj = function(id) {
return self.toEjsObj(self.list[id])
}
this.toEjsObj = function (filter) {
if(!filter.active) {
return false
}
switch(filter.type)
{
case 'time':
return ejs.RangeFilter(filter.field)
.from(filter.from)
.to(filter.to)
break;
case 'range':
return ejs.RangeFilter(filter.field)
.from(filter.from)
.to(filter.to)
break;
case 'querystring':
return ejs.QueryFilter(ejs.QueryStringQuery(filter.query))
break;
case 'terms':
return ejs.TermsFilter(filter.field,filter.value)
break;
case 'exists':
return ejs.ExistsFilter(filter.field)
break;
case 'missing':
return ejs.MissingFilter(filter.field)
break;
default:
return false;
}
}
this.getByType = function(type,inactive) {
return _.pick(self.list,self.idsByType(type,inactive))
}
this.removeByType = function(type) {
var ids = self.idsByType(type)
_.each(ids,function(id) {
self.remove(id)
})
return ids;
}
this.idsByType = function(type,inactive) {
var _require = inactive ? {type:type} : {type:type,active:true}
return _.pluck(_.where(self.list,_require),'id')
}
// This special function looks for all time filters, and returns a time range according to the mode
this.timeRange = function(mode) {
var _t = _.where(self.list,{type:'time',active:true})
if(_t.length == 0) {
return false;
}
switch(mode) {
case "min":
return {
from: new Date(_.max(_.pluck(_t,'from'))),
to: new Date(_.min(_.pluck(_t,'to')))
}
break;
case "max":
return {
from: new Date(_.min(_.pluck(_t,'from'))),
to: new Date(_.max(_.pluck(_t,'to')))
}
break;
default:
return false;
}
}
this.remove = function(id) {
if(!_.isUndefined(self.list[id])) {
delete self.list[id];
// This must happen on the full path also since _.without returns a copy
self.ids = dashboard.current.services.filter.ids = _.without(self.ids,id)
_f.idQueue.unshift(id)
_f.idQueue.sort(function(a,b){return a-b});
return true;
} else {
return false;
}
}
var nextId = function() {
if(_f.idQueue.length > 0) {
return _f.idQueue.shift()
} else {
return self.ids.length;
}
}
// Now init
self.init();
})
.service('dashboard', function($routeParams, $http, $rootScope, $injector, ejsResource, timer, kbnIndex) {
// A hash of defaults to use when loading a dashboard
var _dash = {
title: "",
editable: true,
rows: [],
services: {},
index: {
interval: 'none',
pattern: '_all',
default: '_all'
},
};
// An elasticJS client to use
var ejs = ejsResource(config.elasticsearch);
var gist_pattern = /(^\d{5,}$)|(^[a-z0-9]{10,}$)|(gist.github.com(\/*.*)\/[a-z0-9]{5,}\/*$)/;
// Store a reference to this
var self = this;
var filterSrv,query;
this.current = {};
this.last = {};
$rootScope.$on('$routeChangeSuccess',function(){
// Clear the current dashboard to prevent reloading
self.current = {};
self.indices = [];
route();
})
var route = function() {
// Is there a dashboard type and id in the URL?
if(!(_.isUndefined($routeParams.type)) && !(_.isUndefined($routeParams.id))) {
var _type = $routeParams.type;
var _id = $routeParams.id;
if(_type === 'elasticsearch')
self.elasticsearch_load('dashboard',_id)
if(_type === 'temp')
self.elasticsearch_load('temp',_id)
if(_type === 'file')
self.file_load(_id)
// No dashboard in the URL
} else {
// Check if browser supports localstorage, and if there's a dashboard
if (Modernizr.localstorage &&
!(_.isUndefined(localStorage['dashboard'])) &&
localStorage['dashboard'] !== ''
) {
var dashboard = JSON.parse(localStorage['dashboard']);
_.defaults(dashboard,_dash);
self.dash_load(dashboard)
// No? Ok, grab default.json, its all we have now
} else {
self.file_load('default.json')
}
}
}
// Since the dashboard is responsible for index computation, we can compute and assign the indices
// here before telling the panels to refresh
this.refresh = function() {
if(self.current.index.interval !== 'none') {
if(filterSrv.idsByType('time').length > 0) {
var _range = filterSrv.timeRange('min');
kbnIndex.indices(_range.from,_range.to,
self.current.index.pattern,self.current.index.interval
).then(function (p) {
if(p.length > 0) {
self.indices = p;
} else {
self.indices = [self.current.index.default]
}
$rootScope.$broadcast('refresh')
});
} else {
// This is not optimal, we should be getting the entire index list here, or at least every
// index that possibly matches the pattern
self.indices = [self.current.index.default]
$rootScope.$broadcast('refresh')
}
} else {
self.indices = [self.current.index.default]
$rootScope.$broadcast('refresh')
}
}
this.dash_load = function(dashboard) {
// Cancel all timers
timer.cancel_all();
// If not using time based indices, use the default index
if(dashboard.index.interval === 'none') {
self.indices = [dashboard.index.default]
}
self.current = _.clone(dashboard);
// Ok, now that we've setup the current dashboard, we can inject our services
query = $injector.get('query');
filterSrv = $injector.get('filterSrv')
// Make sure these re-init
query.init();
filterSrv.init();
if(dashboard.index.interval !== 'none' && filterSrv.idsByType('time').length == 0) {
//if(dashboard.index.interval !== 'none') {
self.refresh();
}
return true;
}
this.gist_id = function(string) {
if(self.is_gist(string))
return string.match(gist_pattern)[0].replace(/.*\//, '');
}
this.is_gist = function(string) {
if(!_.isUndefined(string) && string != '' && !_.isNull(string.match(gist_pattern)))
return string.match(gist_pattern).length > 0 ? true : false;
else
return false
}
this.to_file = function() {
var blob = new Blob([angular.toJson(self.current,true)], {type: "application/json;charset=utf-8"});
// from filesaver.js
saveAs(blob, self.current.title+"-"+new Date().getTime());
return true;
}
this.set_default = function(dashboard) {
if (Modernizr.localstorage) {
localStorage['dashboard'] = angular.toJson(dashboard || self.current);
return true;
} else {
return false;
}
}
this.purge_default = function() {
if (Modernizr.localstorage) {
localStorage['dashboard'] = '';
return true;
} else {
return false;
}
}
// TOFIX: Pretty sure this breaks when you're on a saved dashboard already
this.share_link = function(title,type,id) {
return {
location : location.href.replace(location.hash,""),
type : type,
id : id,
link : location.href.replace(location.hash,"")+"#dashboard/"+type+"/"+id,
title : title
};
}
this.file_load = function(file) {
return $http({
url: "dashboards/"+file,
method: "GET",
}).then(function(result) {
var _dashboard = result.data
_.defaults(_dashboard,_dash);
self.dash_load(_dashboard);
return true;
},function(result) {
return false;
});
}
this.elasticsearch_load = function(type,id) {
var request = ejs.Request().indices(config.kibana_index).types(type);
var results = request.query(
ejs.IdsQuery(id)
).doSearch();
return results.then(function(results) {
if(_.isUndefined(results)) {
return false;
} else {
self.dash_load(angular.fromJson(results.hits.hits[0]['_source']['dashboard']))
return true;
}
});
}
this.elasticsearch_save = function(type,title,ttl) {
// Clone object so we can modify it without influencing the existing obejct
var save = _.clone(self.current)
// Change title on object clone
if (type === 'dashboard') {
var id = save.title = _.isUndefined(title) ? self.current.title : title;
}
// Create request with id as title. Rethink this.
var request = ejs.Document(config.kibana_index,type,id).source({
user: 'guest',
group: 'guest',
title: save.title,
dashboard: angular.toJson(save)
})
if (type === 'temp')
request = request.ttl(ttl)
// TOFIX: Implement error handling here
return request.doIndex(
// Success
function(result) {
return result;
},
// Failure
function(result) {
return false;
}
);
}
this.elasticsearch_delete = function(id) {
return ejs.Document(config.kibana_index,'dashboard',id).doDelete(
// Success
function(result) {
return result;
},
// Failure
function(result) {
return false;
}
);
}
this.elasticsearch_list = function(query,count) {
var request = ejs.Request().indices(config.kibana_index).types('dashboard');
return request.query(
ejs.QueryStringQuery(query || '*')
).size(count).doSearch(
// Success
function(result) {
return result;
},
// Failure
function(result) {
return false;
}
);
}
// TOFIX: Gist functionality
this.save_gist = function(title,dashboard) {
var save = _.clone(dashboard || self.current)
save.title = title || self.current.title;
return $http({
url: "https://api.github.com/gists",
method: "POST",
data: {
"description": save.title,
"public": false,
"files": {
"kibana-dashboard.json": {
"content": angular.toJson(save,true)
}
}
}
}).then(function(data, status, headers, config) {
return data.data.html_url;
}, function(data, status, headers, config) {
return false;
});
}
this.gist_list = function(id) {
return $http.jsonp("https://api.github.com/gists/"+id+"?callback=JSON_CALLBACK"
).then(function(response) {
var files = []
_.each(response.data.data.files,function(v,k) {
try {
var file = JSON.parse(v.content)
files.push(file)
} catch(e) {
// Nothing?
}
});
return files;
}, function(data, status, headers, config) {
return false;
});
}
})
.service('keylistener', function($rootScope) {
var keys = [];

View File

@ -5,13 +5,6 @@
Also note that geoJSON is <strong>long,lat NOT lat,long</strong>.
</div>
</div>
<div class="row-fluid">
<div class="span11">
<h6>Query</h6>
<input type="text" style="width:100%" ng-model="panel.query">
</div>
</div>
<div class="row-fluid">
<div class="span4">
<form>

View File

@ -12,17 +12,10 @@
* field :: field containing a 2 element array in the format [lon,lat]
* tooltip :: field to extract the tool tip value from
* spyable :: Show the 'eye' icon that reveals the last ES query
### Group Events
#### Sends
* get_time :: On panel initialization get time range to query
#### Receives
* time :: An object containing the time range to use and the index(es) to query
* query :: An Array of queries, this panel uses only the first one
*/
angular.module('kibana.bettermap', [])
.controller('bettermap', function($scope, eventBus) {
.controller('bettermap', function($scope, query, dashboard, filterSrv) {
// Set and populate defaults
var _d = {
@ -37,44 +30,53 @@ angular.module('kibana.bettermap', [])
_.defaults($scope.panel,_d)
$scope.init = function() {
eventBus.register($scope,'time', function(event,time){set_time(time)});
eventBus.register($scope,'query', function(event, query) {
$scope.panel.query = _.isArray(query) ? query[0] : query;
$scope.$on('refresh',function(){
$scope.get_data();
})
$scope.get_data();
});
// Now that we're all setup, request the time from our group
eventBus.broadcast($scope.$id,$scope.panel.group,'get_time')
}
$scope.get_data = function(segment,query_id) {
$scope.get_data = function(segment,query_id) {
$scope.panel.error = false;
// Make sure we have everything for the request to complete
if(_.isUndefined($scope.index) || _.isUndefined($scope.time))
return
if(dashboard.indices.length == 0) {
return;
}
if(_.isUndefined($scope.panel.field)) {
$scope.panel.error = "Please select a field that contains geo point in [lon,lat] format"
return
}
//$scope.panel.loading = true;
// Determine the field to sort on
var timeField = _.uniq(_.pluck(filterSrv.getByType('time'),'field'))
if(timeField.length > 1) {
$scope.panel.error = "Time field must be consistent amongst time filters"
} else if(timeField.length == 0) {
timeField = null;
} else {
timeField = timeField[0]
}
var _segment = _.isUndefined(segment) ? 0 : segment
$scope.segment = _segment;
var request = $scope.ejs.Request().indices($scope.index[_segment])
var boolQuery = ejs.BoolQuery();
_.each(query.list,function(q) {
boolQuery = boolQuery.should(ejs.QueryStringQuery((q.query || '*')))
})
var request = $scope.ejs.Request().indices(dashboard.indices[_segment])
.query(ejs.FilteredQuery(
ejs.QueryStringQuery(($scope.panel.query || '*') + " AND _exists_:"+$scope.panel.field),
ejs.RangeFilter($scope.time.field)
.from($scope.time.from)
.to($scope.time.to)
)
)
boolQuery,
filterSrv.getBoolFilter(filterSrv.ids).must(ejs.ExistsFilter($scope.panel.field))
))
.fields([$scope.panel.field,$scope.panel.tooltip])
.size($scope.panel.size)
.sort($scope.time.field,'desc');
if(!_.isNull(timeField)) {
request = request.sort(timeField,'desc');
}
$scope.populate_modal(request)
@ -119,7 +121,7 @@ $scope.get_data = function(segment,query_id) {
$scope.$emit('draw')
// Get $size results then stop querying
if($scope.data.length < $scope.panel.size && _segment+1 < $scope.index.length)
if($scope.data.length < $scope.panel.size && _segment+1 < dashboard.indices.length)
$scope.get_data(_segment+1,$scope.query_id)
});
@ -130,24 +132,12 @@ $scope.get_data = function(segment,query_id) {
$scope.modal = {
title: "Inspector",
body : "<h5>Last Elasticsearch Query</h5><pre>"+
'curl -XGET '+config.elasticsearch+'/'+$scope.index+"/_search?pretty -d'\n"+
'curl -XGET '+config.elasticsearch+'/'+dashboard.indices+"/_search?pretty -d'\n"+
angular.toJson(JSON.parse(request.toString()),true)+
"'</pre>",
}
}
function set_time(time) {
$scope.time = time;
$scope.index = _.isUndefined(time.index) ? $scope.index : time.index
$scope.get_data();
}
$scope.build_search = function(field,value) {
$scope.panel.query = add_to_query($scope.panel.query,field,value,false)
$scope.get_data();
eventBus.broadcast($scope.$id,$scope.panel.group,'query',[$scope.panel.query]);
}
})
.directive('bettermap', function() {
return {

View File

@ -28,9 +28,6 @@
<div class="span3" ng-show="panel.load.elasticsearch">
<label class="small">ES list size</label><input class="input-mini" type="number" ng-model="panel.elasticsearch_size">
</div>
<div class="span3" ng-show="panel.load.elasticsearch">
<label class="small">ES store index</label><input class="input-small" type="text" ng-model="panel.elasticsearch_saveto">
</div>
</div>
<h5>Sharing</h5>
<div class="row-fluid">
@ -41,11 +38,4 @@
<label class="small">Shared Link TTL (examples: 1m,1d,1w,30d)</label><input class="input-small" type="text" ng-model="panel.temp_ttl">
</div>
</div>
<h5>Other Settings</h5>
<div class="row-fluid">
<div class="span3" >
<label class="small"> Remove this Dashboard Control from saved copies </label><input type="checkbox" ng-model="panel.hide_control" ng-checked="panel.hide_control">
</div>
</div>
</div>

View File

@ -11,12 +11,12 @@
<h5>Gist <small>Enter a gist number or url</small></h5>
<form>
<input type="text" ng-model="gist.url"/><br>
<button class="btn" ng-click="gist_dblist(gist_id(gist.url))" ng-show="is_gist(gist.url)"><i class="icon-github-alt"></i> Get gist:{{gist.url | gistid}}</button>
<button class="btn" ng-click="gist_dblist(dashboard.gist_id(gist.url))" ng-show="dashboard.is_gist(gist.url)"><i class="icon-github-alt"></i> Get gist:{{gist.url | gistid}}</button>
<h6 ng-show="gist.files.length">Dashboards in gist:{{gist.url | gistid}} <small>click to load</small></h6>
<h6 ng-hide="gist.files.length">No gist dashboards found</h6>
<table class="table table-condensed table-striped">
<tr ng-repeat="file in gist.files">
<td><a ng-click="dash_load(file)">{{file.title}}</a></td>
<td><a ng-click="dashboard.dash_load(file)">{{file.title}}</a></td>
</tr>
</table>
</form>
@ -30,10 +30,10 @@
<h6 ng-show="elasticsearch.dashboards.length">Elasticsearch stored dashboards</h6>
<h6 ng-hide="elasticsearch.dashboards.length">No dashboards matching your query found</h6>
<table class="table table-condensed table-striped">
<tr ng-repeat="dashboard in elasticsearch.dashboards">
<td><a ng-click="elasticsearch_delete(dashboard)"><i class="icon-remove"></i></a></td>
<td><a href="#/dashboard/elasticsearch/{{dashboard._id}}">{{dashboard._id}}</a></td>
<td><a><i class="icon-share" ng-click="share_link(dashboard._id,'elasticsearch',dashboard._id)" bs-modal="'panels/dashcontrol/share.html'"></i></a></td>
<tr ng-repeat="row in elasticsearch.dashboards">
<td><a ng-click="elasticsearch_delete(row._id)"><i class="icon-remove"></i></a></td>
<td><a href="#/dashboard/elasticsearch/{{row._id}}">{{row._id}}</a></td>
<td><a><i class="icon-share" ng-click="share = dashboard.share_link(row._id,'elasticsearch',row._id)" bs-modal="'panels/dashcontrol/share.html'"></i></a></td>
</tr>
</table>
</div>

View File

@ -2,5 +2,5 @@
<label class='small'>Dashboard Control</label>
<button class='btn' ng-show="panel.load.gist || panel.load.elasticsearch || panel.load.local" data-placement="bottom" data-unique="1" ng-click="elasticsearch_dblist(elasticsearch.query)" bs-popover="'panels/dashcontrol/load.html'"><i class='icon-folder-open'></i> <i class='icon-caret-down'></i></button>
<button class='btn' ng-show="panel.save.gist || panel.save.elasticsearch || panel.save.local || panel.save.default" data-placement="bottom" data-unique="1" bs-popover="'panels/dashcontrol/save.html'"><i class='icon-save'></i> <i class='icon-caret-down'></i></button>
<button ng-show="panel.temp" class='btn' ng-click="elasticsearch_save('temp')" bs-modal="'panels/dashcontrol/share.html'"><i class='icon-share'></i></button>
<button ng-show="panel.temp" class='btn' ng-click="elasticsearch_save('temp',panel.temp_ttl)" bs-modal="'panels/dashcontrol/share.html'"><i class='icon-share'></i></button>
</kibana-panel>

View File

@ -17,7 +17,6 @@
** local :: Allow loading of dashboards from Elasticsearch
* hide_control :: Upon save, hide this panel
* elasticsearch_size :: show this many dashboards under the ES section in the load drop down
* elasticsearch_saveto :: Special kibana index to save to
* temp :: Allow saving of temp dashboards
* temp_ttl :: How long should temp dashboards persist
@ -28,7 +27,8 @@
*/
angular.module('kibana.dashcontrol', [])
.controller('dashcontrol', function($scope, $routeParams, $http, eventBus, timer) {
.controller('dashcontrol', function($scope, $http, timer, dashboard) {
$scope.panel = $scope.panel || {};
// Set and populate defaults
var _d = {
@ -47,7 +47,6 @@ angular.module('kibana.dashcontrol', [])
},
hide_control: false,
elasticsearch_size: 20,
elasticsearch_saveto: $scope.config.kibana_index,
temp: true,
temp_ttl: '30d'
}
@ -57,241 +56,101 @@ angular.module('kibana.dashcontrol', [])
var _dash = {
title: "",
editable: true,
rows: []
rows: [],
services: {}
}
$scope.init = function() {
// Long ugly if statement for figuring out which dashboard to load on init
// If there is no dashboard defined, find one
if(_.isUndefined($scope.dashboards)) {
// First check the URL for a path to a dashboard
if(!(_.isUndefined($routeParams.type)) && !(_.isUndefined($routeParams.id))) {
var _type = $routeParams.type;
var _id = $routeParams.id;
if(_type === 'elasticsearch')
$scope.elasticsearch_load('dashboard',_id)
if(_type === 'temp')
$scope.elasticsearch_load('temp',_id)
if(_type === 'file')
$scope.file_load(_id)
// No dashboard in the URL
} else {
// Check if browser supports localstorage, and if there's a dashboard
if (Modernizr.localstorage &&
!(_.isUndefined(localStorage['dashboard'])) &&
localStorage['dashboard'] !== ''
) {
var dashboard = JSON.parse(localStorage['dashboard']);
_.defaults(dashboard,_dash);
$scope.dash_load(JSON.stringify(dashboard))
// No? Ok, grab default.json, its all we have now
} else {
$scope.file_load('default')
}
}
}
$scope.gist_pattern = /(^\d{5,}$)|(^[a-z0-9]{10,}$)|(gist.github.com(\/*.*)\/[a-z0-9]{5,}\/*$)/;
$scope.gist = {};
$scope.elasticsearch = {};
}
$scope.to_file = function() {
var blob = new Blob([angular.toJson($scope.dashboards,true)], {type: "application/json;charset=utf-8"});
saveAs(blob, $scope.dashboards.title+"-"+new Date().getTime());
}
$scope.default = function() {
if (Modernizr.localstorage) {
localStorage['dashboard'] = angular.toJson($scope.dashboards);
$scope.alert('Success',
$scope.dashboards.title + " has been set as your default dashboard",
'success',5000)
$scope.set_default = function() {
if(dashboard.set_default()) {
$scope.alert('Local Default Set',dashboard.current.title+' has been set as your local default','success',5000)
} else {
$scope.alert('Bummer!',
"Your browser is too old for this functionality",
'error',5000);
$scope.alert('Incompatible Browser','Sorry, your browser is too old for this feature','error',5000)
}
}
$scope.share_link = function(title,type,id) {
$scope.share = {
location : location.href.replace(location.hash,""),
type : type,
id : id,
link : location.href.replace(location.hash,"")+"#dashboard/"+type+"/"+id,
title : title
};
}
$scope.purge = function() {
if (Modernizr.localstorage) {
localStorage['dashboard'] = '';
$scope.alert('Success',
'Default dashboard cleared',
'success',5000)
$scope.purge_default = function() {
if(dashboard.purge_default()) {
$scope.alert('Local Default Clear','Your local default dashboard has been cleared','success',5000)
} else {
$scope.alert('Doh!',
"Your browser is too old for this functionality",
'error',5000);
$scope.alert('Incompatible Browser','Sorry, your browser is too old for this feature','error',5000)
}
}
$scope.file_load = function(file) {
$http({
url: "dashboards/"+file,
method: "GET",
}).success(function(data, status, headers, config) {
var dashboard = data
_.defaults(dashboard,_dash);
$scope.dash_load(JSON.stringify(dashboard))
}).error(function(data, status, headers, config) {
$scope.alert('Default dashboard missing!','Could not locate dashboards/'+file,'error')
});
$scope.elasticsearch_save = function(type,ttl) {
dashboard.elasticsearch_save(type,($scope.elasticsearch.title || dashboard.current.title),ttl).then(
function(result) {
if(!_.isUndefined(result._id)) {
$scope.alert('Dashboard Saved','This dashboard has been saved to Elasticsearch as "' +
result._id + '"','success',5000)
if(type === 'temp') {
$scope.share = dashboard.share_link(dashboard.current.title,'temp',result._id)
}
$scope.elasticsearch_save = function(type) {
// Clone object so we can modify it without influencing the existing obejct
if($scope.panel.hide_control) {
$scope.panel.hide = true;
var save = _.clone($scope.dashboards)
} else {
var save = _.clone($scope.dashboards)
$scope.alert('Save failed','Dashboard could not be saved to Elasticsearch','error',5000)
}
// Change title on object clone
if(type === 'dashboard')
var id = save.title = $scope.elasticsearch.title;
// Create request with id as title. Rethink this.
var request = $scope.ejs.Document($scope.panel.elasticsearch_saveto,type,id).source({
user: 'guest',
group: 'guest',
title: save.title,
dashboard: angular.toJson(save)
})
if(type === 'temp')
request = request.ttl($scope.panel.temp_ttl)
var result = request.doIndex();
var id = result.then(function(result) {
$scope.alert('Dashboard Saved','This dashboard has been saved to Elasticsearch','success',5000)
$scope.elasticsearch_dblist($scope.elasticsearch.query);
$scope.elasticsearch.title = '';
if(type === 'temp')
$scope.share_link($scope.dashboards.title,'temp',result._id)
})
$scope.panel.hide = false;
}
$scope.elasticsearch_delete = function(dashboard) {
var result = $scope.ejs.Document($scope.panel.elasticsearch_saveto,'dashboard',dashboard._id).doDelete();
result.then(function(result) {
$scope.alert('Dashboard Deleted','','success',5000)
$scope.elasticsearch.dashboards = _.without($scope.elasticsearch.dashboards,dashboard)
})
}
$scope.elasticsearch_load = function(type,id) {
var request = $scope.ejs.Request().indices($scope.panel.elasticsearch_saveto).types(type);
var results = request.query(
$scope.ejs.IdsQuery(id)
).size($scope.panel.elasticsearch_size).doSearch();
results.then(function(results) {
if(_.isUndefined(results)) {
return;
$scope.elasticsearch_delete = function(id) {
dashboard.elasticsearch_delete(id).then(
function(result) {
if(!_.isUndefined(result)) {
if(result.found) {
$scope.alert('Dashboard Deleted',id+' has been deleted','success',5000)
// Find the deleted dashboard in the cached list and remove it
var toDelete = _.where($scope.elasticsearch.dashboards,{_id:id})[0]
$scope.elasticsearch.dashboards = _.without($scope.elasticsearch.dashboards,toDelete)
} else {
$scope.alert('Dashboard Not Found','Could not find '+id+' in Elasticsearch','warning',5000)
}
$scope.panel.error = false;
$scope.dash_load(results.hits.hits[0]['_source']['dashboard'])
});
} else {
$scope.alert('Dashboard Not Deleted','An error occurred deleting the dashboard',error,5000)
}
}
)
}
$scope.elasticsearch_dblist = function(query) {
if($scope.panel.load.elasticsearch) {
var request = $scope.ejs.Request().indices($scope.panel.elasticsearch_saveto).types('dashboard');
var results = request.query(
$scope.ejs.QueryStringQuery(query || '*')
).size($scope.panel.elasticsearch_size).doSearch();
results.then(function(results) {
if(_.isUndefined(results.hits)) {
return;
}
dashboard.elasticsearch_list(query,$scope.panel.elasticsearch_size).then(
function(result) {
if(!_.isUndefined(result.hits)) {
$scope.panel.error = false;
$scope.hits = results.hits.total;
$scope.elasticsearch.dashboards = results.hits.hits
});
$scope.hits = result.hits.total;
$scope.elasticsearch.dashboards = result.hits.hits
}
})
}
$scope.save_gist = function() {
var save = _.clone($scope.dashboards)
save.title = $scope.gist.title;
$http({
url: "https://api.github.com/gists",
method: "POST",
data: {
"description": save.title,
"public": false,
"files": {
"kibana-dashboard.json": {
"content": angular.toJson(save,true)
dashboard.save_gist($scope.gist.title).then(
function(link) {
if(!_.isUndefined(link)) {
$scope.gist.last = link;
$scope.alert('Gist saved','You will be able to access your exported dashboard file at <a href="'+link+'">'+link+'</a> in a moment','success');
} else {
$scope.alert('Save failed','Gist could not be saved','error',5000)
}
}
}
}).success(function(data, status, headers, config) {
$scope.gist.last = data.html_url;
$scope.alert('Gist saved','You will be able to access your exported dashboard file at <a href="'+data.html_url+'">'+data.html_url+'</a> in a moment','success')
}).error(function(data, status, headers, config) {
$scope.alert('Unable to save','Save to gist failed for some reason','error',5000)
});
})
}
$scope.gist_dblist = function(id) {
$http.jsonp("https://api.github.com/gists/"+id+"?callback=JSON_CALLBACK"
).success(function(response) {
$scope.gist.files = []
_.each(response.data.files,function(v,k) {
try {
var file = JSON.parse(v.content)
$scope.gist.files.push(file)
} catch(e) {
$scope.alert('Gist failure','The dashboard file is invalid','warning',5000)
}
});
}).error(function(data, status, headers, config) {
dashboard.gist_list(id).then(
function(files) {
if(files && files.length > 0) {
$scope.gist.files = files;
} else {
$scope.alert('Gist Failed','Could not retrieve dashboard list from gist','error',5000)
});
}
$scope.dash_load = function(dashboard) {
if(!_.isObject(dashboard))
dashboard = JSON.parse(dashboard)
eventBus.broadcast($scope.$id,'ALL','dashboard',{
dashboard : dashboard,
last : $scope.dashboards
})
timer.cancel_all();
}
$scope.gist_id = function(string) {
if($scope.is_gist(string))
return string.match($scope.gist_pattern)[0].replace(/.*\//, '');
}
$scope.is_gist = function(string) {
if(!_.isUndefined(string) && string != '' && !_.isNull(string.match($scope.gist_pattern)))
return string.match($scope.gist_pattern).length > 0 ? true : false;
else
return false
}
})
.directive('dashUpload', function(timer, eventBus){
.directive('dashUpload', function(timer, dashboard){
return {
restrict: 'A',
link: function(scope, elem, attrs) {
@ -304,7 +163,7 @@ angular.module('kibana.dashcontrol', [])
var reader = new FileReader();
reader.onload = (function(theFile) {
return function(e) {
scope.dash_load(JSON.parse(e.target.result))
dashboard.dash_load(JSON.parse(e.target.result))
scope.$apply();
};
})(f);

View File

@ -6,9 +6,9 @@
<h5>Locally</h5>
<form>
<ul class="nav nav-list">
<li><a ng-show="panel.save.local" ng-click="to_file()"><i class="icon-download"></i> Export to File</a></li>
<li><a ng-show="panel.save.default" ng-click="default()"><i class="icon-bookmark"></i> Set as My Default</a></li>
<li><a ng-show="panel.save.default" ng-click="purge()"><i class="icon-ban-circle"></i> Clear My Default</a></li>
<li><a ng-show="panel.save.local" ng-click="dashboard.to_file()"><i class="icon-download"></i> Export to File</a></li>
<li><a ng-show="panel.save.default" ng-click="set_default()"><i class="icon-bookmark"></i> Set as My Default</a></li>
<li><a ng-show="panel.save.default" ng-click="purge_default()"><i class="icon-ban-circle"></i> Clear My Default</a></li>
</ul>
</form>
</div>

View File

@ -4,7 +4,7 @@
</div>
<div class="modal-body">
<label>Share this dashboard with this URL</label>
<input ng-model='share.link' type="text" style="width:90%" onclick="this.select()" onfocus="this.select()" ng-change="share_link(share.title,share.type,share.id)">
<input ng-model='share.link' type="text" style="width:90%" onclick="this.select()" onfocus="this.select()" ng-change="share = dashboard.share_link(share.title,share.type,share.id)">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-success" ng-click="dismiss();$broadcast('render')">Close</button>

View File

@ -1,22 +1,26 @@
<div>
<div class="row-fluid">
<div class="span12">
The derive queries panel takes a query and a field, then runs a terms facet against both and generates a list of terms to query on. For example, you might want to see a histogram of the top 5 requests that return a 404. <strong>You should be careful not to select a high cardinality field</strong> as Elasticsearch must load all of these values into memory.<p>
Query Mode allows to optionally append original query to each term in the list.
</div>
</div>
<div class="row-fluid">
<div class="span1">
<label class="small">Length</label>
<input type="number" style="width:80%" ng-model="panel.size" ng-change="set_refresh(true)">
</div>
<div class="span3">
<label class="small">Field</label>
<input type="text" bs-typeahead="fields.list" style="width:80%" ng-change="set_refresh(true)" ng-model='panel.field'></select>
</div>
<div class="span3">
<label class="small">Query Mode</label>
<select style="width:80%" ng-change="set_refresh(true)" ng-model='panel.mode' ng-options="f for f in ['terms only','AND', 'OR']"></select>
</div>
<div class="span8">
<div class="span5">
<label class="small">Exclude Terms(s) (comma seperated)</label>
<input array-join type="text" style="width:90%" ng-change="set_refresh(true)" ng-model='panel.exclude'></input>
</div>
</div>
<div class="row-fluid">
<div class="span12">
The derive queries panel takes a query and a field, runs a terms facet, then creates queries based on them. For example, you might want to see a histogram of the top 5 requests that return a 404. <strong>You should be careful not to select a high cardinality field</strong> as Elasticsearch must load all of these values into memory.<p>
Query Mode allows to optionally append original query to each term in the list.
</div>
</div>
</div>

View File

@ -1,8 +1,23 @@
<kibana-panel ng-controller='derivequeries' ng-init="init()">
<style>
.end-derive {
position:absolute;
right:15px;
top:5px;
}
.panel-derive {
padding-right: 35px !important;
height: 31px !important;
-webkit-box-sizing: border-box; /* Safari/Chrome, other WebKit */
-moz-box-sizing: border-box; /* Firefox, other Gecko */
box-sizing: border-box; /* Opera/IE 8+ */
}
</style>
<span ng-show='panel.spyable' style="position:absolute;right:0px;top:0px" class='panelextra pointer'>
<i bs-modal="'partials/modal.html'" class="icon-eye-open"></i>
</span>
<div ng-show="!panel.multi">
<!--
<div>
<form>
<table class="form-horizontal">
<tr>
@ -29,4 +44,16 @@
</table>
</form>
</div>
-->
<label class="small">Create new queries from <strong>{{panel.field}}</strong> ({{panel.mode}} mode)</label>
<div>
<form class="form-search" style="position:relative" ng-submit="get_data()">
<input class="search-query panel-derive input-block-level" bs-typeahead="panel.history" data-min-length=0 data-items=100 type="text" ng-model="panel.query"/>
<span class="end-derive">
<i class="icon-search pointer" ng-click="get_data()"></i>
</span
</form>
</div>
</kibana-panel>

View File

@ -1,6 +1,6 @@
/*
## Termsquery
## Derivequeries
Broadcasts an array of queries based on the results of a terms facet
@ -12,24 +12,18 @@
* fields :: a list of fields known to us
* query_mode :: how to create query
### Group Events
#### Sends
* query :: Always broadcast as an array, even in multi: false
* get_time :: Request the time object from the timepicker
#### Receives
* query :: An array of queries. This is probably needs to be fixed.
* time :: populate index and time
* fields :: A list of fields known to us
*/
angular.module('kibana.derivequeries', [])
.controller('derivequeries', function($scope, eventBus) {
.controller('derivequeries', function($scope, $rootScope, query, fields, dashboard, filterSrv) {
// Set and populate defaults
var _d = {
loading : false,
status : "Beta",
label : "Search",
query : "*",
ids : [],
group : "default",
field : '_type',
fields : [],
@ -43,26 +37,19 @@ angular.module('kibana.derivequeries', [])
_.defaults($scope.panel,_d);
$scope.init = function() {
eventBus.register($scope,'fields', function(event, fields) {
$scope.panel.fields = fields.all;
});
eventBus.register($scope,'time', function(event,time){set_time(time)});
eventBus.register($scope,'query', function(event, query) {
$scope.panel.query = _.isArray(query) ? query[0] : query;
$scope.get_data();
});
// Now that we're all setup, request the time from our group
eventBus.broadcast($scope.$id,$scope.panel.group,'get_time')
$scope.panel.fields = fields.list
}
$scope.get_data = function() {
update_history($scope.panel.query);
// Make sure we have everything for the request to complete
if(_.isUndefined($scope.index) || _.isUndefined($scope.time))
if(dashboard.indices.length == 0) {
return
}
$scope.panel.loading = true;
var request = $scope.ejs.Request().indices($scope.index);
var request = $scope.ejs.Request().indices(dashboard.indices);
// Terms mode
request = request
@ -73,9 +60,7 @@ angular.module('kibana.derivequeries', [])
.facetFilter(ejs.QueryFilter(
ejs.FilteredQuery(
ejs.QueryStringQuery($scope.panel.query || '*'),
ejs.RangeFilter($scope.time.field)
.from($scope.time.from)
.to($scope.time.to)
filterSrv.getBoolFilter(filterSrv.ids)
)))).size(0)
$scope.populate_modal(request);
@ -93,10 +78,22 @@ angular.module('kibana.derivequeries', [])
} else if ($scope.panel.mode === 'OR') {
var suffix = ' OR (' + $scope.panel.query + ')';
}
var ids = [];
_.each(results.facets.query.terms, function(v) {
data.push($scope.panel.field+':"'+v.term+'"'+suffix)
var _q = $scope.panel.field+':"'+v.term+'"'+suffix;
// if it isn't in the list, remove it
var _iq = query.findQuery(_q)
if(!_iq) {
ids.push(query.set({query:_q}));
} else {
ids.push(_iq.id);
}
});
$scope.send_query(data)
_.each(_.difference($scope.panel.ids,ids),function(id){
query.remove(id)
})
$scope.panel.ids = ids;
dashboard.refresh();
});
}
@ -114,23 +111,12 @@ angular.module('kibana.derivequeries', [])
$scope.modal = {
title: "Inspector",
body : "<h5>Last Elasticsearch Query</h5><pre>"+
'curl -XGET '+config.elasticsearch+'/'+$scope.index+"/_search?pretty -d'\n"+
'curl -XGET '+config.elasticsearch+'/'+dashboard.indices+"/_search?pretty -d'\n"+
angular.toJson(JSON.parse(request.toString()),true)+
"'</pre>",
}
}
function set_time(time) {
$scope.time = time;
$scope.index = _.isUndefined(time.index) ? $scope.index : time.index
$scope.get_data();
}
$scope.send_query = function(query) {
var _query = _.isArray(query) ? query : [query]
eventBus.broadcast($scope.$id,$scope.panel.group,'query',_query)
}
var update_history = function(query) {
query = _.isArray(query) ? query : [query];
if($scope.panel.remember > 0) {

View File

@ -1,8 +1,8 @@
<a class="close" ng-click="dismiss()" href="">×</a>
<h4>
Micro Analysis of {{micropanel.field}}
<i class="pointer icon-search" ng-click="build_search('_exists_',micropanel.field);dismiss();"></i>
<i class="pointer icon-ban-circle" ng-click="build_search('_missing_',micropanel.field);dismiss();"></i>
<i class="pointer icon-search" ng-click="fieldExists(micropanel.field,'must');dismiss();"></i>
<i class="pointer icon-ban-circle" ng-click="fieldExists(micropanel.field,'mustNot');dismiss();"></i>
<br><small>{{micropanel.count}} events in the table set</small>
</h4>
<table style="width:480px" class='table table-bordered table-striped table-condensed'>
@ -15,8 +15,8 @@
<tr ng-repeat='field in micropanel.values'>
<td>{{{true: "__blank__",false:field[0]}[field[0] == ""]}}</td>
<td>
<i class="pointer icon-search" ng-click="build_search(micropanel.field,field[0]);dismiss();"></i>
<i class="pointer icon-ban-circle" ng-click="build_search(micropanel.field,field[0],true);dismiss();"></i>
<i class="pointer icon-search" ng-click="build_search(micropanel.field,field[0],'must');dismiss();"></i>
<i class="pointer icon-ban-circle" ng-click="build_search(micropanel.field,field[0],'mustNot');dismiss();"></i>
</td>
<td>{{field[1]}}</td>
</tr>

View File

@ -18,7 +18,7 @@
*/
angular.module('kibana.fields', [])
.controller('fields', function($scope, eventBus, $timeout) {
.controller('fields', function($scope, eventBus, $timeout, dashboard, query, filterSrv) {
// Set and populate defaults
var _d = {
@ -79,9 +79,16 @@ angular.module('kibana.fields', [])
eventBus.broadcast($scope.$id,$scope.panel.group,"selected_fields",$scope.active)
}
$scope.build_search = function(field, value,negate) {
$scope.panel.query = [add_to_query($scope.panel.query,field,value,negate)]
eventBus.broadcast($scope.$id,$scope.panel.group,'query',$scope.panel.query);
$scope.build_search = function(field,value,mandate) {
var query = field+":"+angular.toJson(value)
filterSrv.set({type:'querystring',query:query,mandate:mandate})
dashboard.refresh();
}
$scope.fieldExists = function(field,mandate) {
filterSrv.set({type:'exists',field:field,mandate:mandate})
dashboard.refresh();
}
$scope.is_active = function(field) {

View File

@ -0,0 +1,7 @@
<div>
<div class="row-fluid">
<div class="span12">
No options here
</div>
</div>
</div>

View File

@ -0,0 +1,15 @@
<div>
<style>
.input-query-alias {
margin-bottom: 5px !important;
}
</style>
<a class="close" ng-click="render();dismiss();" href="">×</a>
<h6>Query Alias</h6>
<form>
<input class="input-medium input-query-alias" type="text" ng-model="queries.list[id].alias" placeholder='Alias...' />
<div>
<i ng-repeat="color in queries.colors" class="pointer" ng-class="{'icon-circle-blank':queries.list[id].color == color,'icon-circle':queries.list[id].color != color}" style="color:{{color}}" ng-click="queries.list[id].color = color;render();"> </i>
</div>
</form>
</div>

View File

@ -0,0 +1,45 @@
<kibana-panel ng-controller='filtering' ng-init="init()">
<style>
.filtering-container {
margin-top: 3px;
}
.filter-panel-filter {
display:inline-block;
vertical-align: top;
margin-left: 10px;
width: 200px;
padding: 5px;
border: #555 1px solid;
margin: 0px 5px 5px 0px;
}
.filter-must {
border-bottom: #7EB26D 3px solid;
}
.filter-mustNot {
border-bottom: #E24D42 3px solid;
}
.filter-should {
border-bottom: #EF843C 3px solid;
}
.filter-action {
float:right;
margin-bottom: 0px !important;
margin-left: 3px;
}
</style>
<div class='filtering-container'>
<div ng-repeat="id in filterSrv.ids" class="small filter-panel-filter">
<div class="filter-{{filterSrv.list[id].mandate}}">
<strong>{{filterSrv.list[id].type}}</strong> {{filterSrv.list[id].mandate}}
<i class="filter-action pointer icon-remove" bs-tooltip="'Remove'" ng-click="remove(id)"></i>
<i class="filter-action pointer" ng-class="{'icon-check': filterSrv.list[id].active,'icon-check-empty': !filterSrv.list[id].active}" bs-tooltip="'Toggle'" ng-click="toggle(id)"></i>
</div>
<ul class="unstyled">
<li ng-repeat="(key,value) in filterSrv.list[id]" ng-show="show_key(key)"><strong>{{key}}</strong> : {{value}}</li>
</ul>
</div>
</div>
</kibana-panel>

View File

@ -0,0 +1,46 @@
/*
## filtering
An experimental for interacting with the filter service
### Parameters
*/
angular.module('kibana.filtering', [])
.controller('filtering', function($scope, filterSrv, $rootScope, dashboard) {
// Set and populate defaults
var _d = {
status : "Experimental"
}
_.defaults($scope.panel,_d);
$scope.init = function() {
$scope.filterSrv = filterSrv
}
$scope.remove = function(id) {
filterSrv.remove(id);
dashboard.refresh();
}
$scope.toggle = function(id) {
filterSrv.list[id].active = !filterSrv.list[id].active;
dashboard.refresh();
}
$scope.refresh = function(query) {
$rootScope.$broadcast('refresh')
}
$scope.render = function(query) {
$rootScope.$broadcast('render')
}
$scope.show_key = function(key) {
return !_.contains(['type','id','alias','mandate','active'],key)
}
});

View File

@ -4,48 +4,22 @@
<label class="small">Mode</label>
<select ng-change="set_refresh(true)" class="input-small" ng-model="panel.mode" ng-options="f for f in ['count','min','mean','max','total']"></select>
</div>
<div class="span3" ng-show="panel.mode != 'count'">
<label class="small">Field</label>
<div class="span2">
<label class="small">Time Field</label>
<form>
<input ng-change="set_refresh(true)" placeholder="Start typing" bs-typeahead="fields.list" type="text" class="input-small" ng-model="panel.time_field">
</form>
</div>
<div class="span2" ng-show="panel.mode != 'count'">
<label class="small">Value Field</label>
<form>
<input ng-change="set_refresh(true)" placeholder="Start typing" bs-typeahead="fields.list" type="text" class="input-small" ng-model="panel.value_field">
</form>
</div>
<div class="span5" ng-show="panel.mode != 'count'">
<div class="span3" ng-show="panel.mode != 'count'">
<label class="small">Note</label><small> In <strong>{{panel.mode}}</strong> mode the configured field <strong>must</strong> be a numeric type</small>
</div>
</div>
<div class="row-fluid">
<div class="span3">
<form style="margin-bottom: 0px">
<label class="small">Label</label>
<input type="text" placeholder="New Label" style="width:70%" ng-model="newlabel">
</form>
</div>
<div class="span8">
<label class="small">Query</label>
<form class="input-append" style="margin-bottom: 0px">
<input type="text" placeholder="New Query" style="width:80%" ng-model="newquery">
<button class="btn" ng-click="add_query(newlabel,newquery);newlabel='';newquery='';set_refresh(true)"><i class="icon-plus"></i></button>
</form>
</div>
<div class="span1">
</div>
</div>
<div class="row-fluid" ng-repeat="q in panel.query">
<div class="span3">
<form style="margin-bottom: 0px">
<input type="text" style="width:70%" ng-model="q.label" ng-change="set_refresh(true)">
</form>
</div>
<div class="span8">
<form style="margin-bottom: 0px">
<input type="text" style="width:80%" ng-model="q.query" ng-change="set_refresh(true)">
</form>
</div>
<div class="span1">
<i class="icon-remove pointer" ng-click="remove_query(q)"></i>
</div>
</div>
<h5>Chart Options</h5>
<div class="row-fluid" style="margin-bottom:10px;">
<div class="span1"> <label class="small">Bars</label><input type="checkbox" ng-model="panel.bars" ng-checked="panel.bars"></div>

View File

@ -1,18 +1,36 @@
<kibana-panel ng-controller='histogram' ng-init="init()" style="height:{{panel.height || row.height}}">
<span ng-show="panel.spyable" style="position:absolute;right:0px;top:0px" class='panelextra pointer'>
<style>
.histogram-legend {
display:inline-block;
padding-right:5px
}
.histogram-legend-dot {
display:inline-block;
height:10px;
width:10px;
border-radius:5px;
}
.histogram-legend-item {
display:inline-block;
}
.histogram-chart {
position:relative;
}
</style>
<span ng-show="panel.spyable" class='spy panelextra pointer'>
<i bs-modal="'partials/modal.html'" class="icon-eye-open"></i>
</span>
<div>
<span ng-show='panel.zoomlinks && data'>
<a class='small' ng-click='zoom(0.5)'><i class='icon-zoom-in'></i> Zoom In</a>
<!--<a class='small' ng-click='zoom(0.5)'><i class='icon-zoom-in'></i> Zoom In</a>-->
<a class='small' ng-click='zoom(2)'><i class='icon-zoom-out'></i> Zoom Out</a> |
</span>
<span ng-show="panel.legend" ng-repeat='series in plot.getData()' style='display:inline-block;padding-right:5px'>
<div style="display:inline-block;background:{{series.color}};height:10px;width:10px;border-radius:5px;"></div>
<div class='small' style='display:inline-block'>{{series.label}} ({{series.hits}})</div>
<span ng-show="panel.legend" ng-repeat='series in data' class="histogram-legend">
<div class="histogram-legend-dot" style="background:{{series.info.color}};"></div>
<div class='small histogram-legend-item'>{{series.info.alias}} ({{series.hits}})</div>
</span>
<span ng-show="panel.legend" class="small"><span ng-show="panel.value_field && panel.mode != 'count'">{{panel.value_field}}</span> {{panel.mode}} per <strong>{{panel.interval}}</strong> | (<strong>{{hits}}</strong> hits)</span>
</div>
<center><img ng-show='panel.loading && _.isUndefined(data)' src="common/img/load_big.gif"></center>
<div histogram-chart params="{{panel}}" style="height:{{panel.height || row.height}};position:relative"></div>
<div histogram-chart class="histogram-chart" params="{{panel}}" style="height:{{panel.height || row.height}};"></div>
</kibana-panel>

View File

@ -32,17 +32,11 @@
* x-axis :: Show x-axis labels and grid lines
* y-axis :: Show y-axis labels and grid lines
* interactive :: Allow drag to select time range
### Group Events
#### Receives
* time :: An object containing the time range to use and the index(es) to query
* query :: An Array of queries, even if its only one
#### Sends
* get_time :: On panel initialization get time range to query
*/
angular.module('kibana.histogram', [])
.controller('histogram', function($scope, eventBus) {
.controller('histogram', function($scope, eventBus, query, dashboard, filterSrv) {
// Set and populate defaults
var _d = {
@ -50,11 +44,12 @@ angular.module('kibana.histogram', [])
group : "default",
query : [ {query: "*", label:"Query"} ],
mode : 'count',
time_field : '@timestamp',
value_field : null,
auto_int : true,
resolution : 100,
interval : '5m',
fill : 3,
fill : 0,
linewidth : 3,
timezone : 'browser', // browser, utc or a standard timezone
spyable : true,
@ -72,82 +67,55 @@ angular.module('kibana.histogram', [])
_.defaults($scope.panel,_d)
$scope.init = function() {
eventBus.register($scope,'time', function(event,time){$scope.set_time(time)});
// Consider eliminating the check for array, this should always be an array
eventBus.register($scope,'query', function(event, query) {
if(_.isArray(query)) {
$scope.panel.query = _.map(query,function(q) {
return {query: q, label: q};
$scope.queries = query;
$scope.$on('refresh',function(){
$scope.get_data();
})
} else {
$scope.panel.query[0] = {query: query, label: query}
}
$scope.get_data();
});
// Now that we're all setup, request the time from our group if we don't
// have it yet
if(_.isUndefined($scope.time))
eventBus.broadcast($scope.$id,$scope.panel.group,'get_time')
}
$scope.get_data()
$scope.remove_query = function(q) {
$scope.panel.query = _.without($scope.panel.query,q);
$scope.get_data();
}
$scope.add_query = function(label,query) {
if(!(_.isArray($scope.panel.query)))
$scope.panel.query = new Array();
$scope.panel.query.unshift({
query: query,
label: label,
});
$scope.get_data();
}
$scope.get_data = function(segment,query_id) {
delete $scope.panel.error
// Make sure we have everything for the request to complete
if(_.isUndefined($scope.index) || _.isUndefined($scope.time))
if(dashboard.indices.length == 0) {
return
}
var _range = $scope.range = filterSrv.timeRange('min');
if ($scope.panel.auto_int)
$scope.panel.interval = secondsToHms(calculate_interval($scope.time.from,$scope.time.to,$scope.panel.resolution,0)/1000);
$scope.panel.interval = secondsToHms(calculate_interval(_range.from,_range.to,$scope.panel.resolution,0)/1000);
$scope.panel.loading = true;
var _segment = _.isUndefined(segment) ? 0 : segment
var request = $scope.ejs.Request().indices($scope.index[_segment]);
var request = $scope.ejs.Request().indices(dashboard.indices[_segment]);
// Build the question part of the query
var queries = [];
_.each($scope.panel.query, function(v) {
queries.push($scope.ejs.FilteredQuery(
ejs.QueryStringQuery(v.query || '*'),
ejs.RangeFilter($scope.time.field)
.from($scope.time.from)
.to($scope.time.to))
// Build the query
_.each($scope.queries.ids, function(id) {
var query = $scope.ejs.FilteredQuery(
ejs.QueryStringQuery($scope.queries.list[id].query || '*'),
filterSrv.getBoolFilter(filterSrv.ids)
)
});
// Build the facet part, injecting the query in as a facet filter
_.each(queries, function(v) {
var facet = $scope.ejs.DateHistogramFacet("chart"+_.indexOf(queries,v))
var facet = $scope.ejs.DateHistogramFacet(id)
if($scope.panel.mode === 'count') {
facet = facet.field($scope.time.field)
facet = facet.field($scope.panel.time_field)
} else {
if(_.isNull($scope.panel.value_field)) {
$scope.panel.error = "In " + $scope.panel.mode + " mode a field must be specified";
return
}
facet = facet.keyField($scope.time.field).valueField($scope.panel.value_field)
facet = facet.keyField($scope.panel.time_field).valueField($scope.panel.value_field)
}
facet = facet.interval($scope.panel.interval).facetFilter($scope.ejs.QueryFilter(v))
facet = facet.interval($scope.panel.interval).facetFilter($scope.ejs.QueryFilter(query))
request = request.facet(facet).size(0)
})
});
// Populate the inspector panel
$scope.populate_modal(request);
@ -157,6 +125,7 @@ angular.module('kibana.histogram', [])
// Populate scope when we have results
results.then(function(results) {
$scope.panel.loading = false;
if(_segment == 0) {
$scope.hits = 0;
@ -170,15 +139,24 @@ angular.module('kibana.histogram', [])
return;
}
// Make sure we're still on the same query
if($scope.query_id === query_id) {
// Convert facet ids to numbers
var facetIds = _.map(_.keys(results.facets),function(k){return parseInt(k);})
// Make sure we're still on the same query/queries
if($scope.query_id === query_id &&
_.intersection(facetIds,query.ids).length == query.ids.length
) {
var i = 0;
_.each(results.facets, function(v, k) {
_.each(query.ids, function(id) {
var v = results.facets[id];
// Null values at each end of the time range ensure we see entire range
if(_.isUndefined($scope.data[i]) || _segment == 0) {
var data = [[$scope.time.from.getTime(), null],[$scope.time.to.getTime(), null]];
var data = []
if(filterSrv.idsByType('time').length > 0) {
data = [[_range.from.getTime(), null],[_range.to.getTime(), null]];
}
var hits = 0;
} else {
var data = $scope.data[i].data
@ -197,15 +175,12 @@ angular.module('kibana.histogram', [])
// Create the flot series object
var series = {
data: {
label: $scope.panel.query[i].label || "query"+(parseInt(i)+1),
info: $scope.queries.list[id],
data: data,
hits: hits
},
};
if (!(_.isUndefined($scope.panel.query[i].color)))
series.data.color = $scope.panel.query[i].color;
$scope.data[i] = series.data
i++;
@ -215,7 +190,7 @@ angular.module('kibana.histogram', [])
$scope.$emit('render')
// If we still have segments left, get them
if(_segment < $scope.index.length-1) {
if(_segment < dashboard.indices.length-1) {
$scope.get_data(_segment+1,query_id)
}
@ -226,7 +201,33 @@ angular.module('kibana.histogram', [])
// function $scope.zoom
// factor :: Zoom factor, so 0.5 = cuts timespan in half, 2 doubles timespan
$scope.zoom = function(factor) {
eventBus.broadcast($scope.$id,$scope.panel.group,'zoom',factor);
var _now = Date.now();
var _range = filterSrv.timeRange('min');
var _timespan = (_range.to.valueOf() - _range.from.valueOf());
var _center = _range.to.valueOf() - _timespan/2
var _to = (_center + (_timespan*factor)/2)
var _from = (_center - (_timespan*factor)/2)
// If we're not already looking into the future, don't.
if(_to > Date.now() && _range.to < Date.now()) {
var _offset = _to - Date.now()
_from = _from - _offset
_to = Date.now();
}
if(factor > 1) {
filterSrv.removeByType('time')
}
filterSrv.set({
type:'time',
from:moment.utc(_from),
to:moment.utc(_to),
field:$scope.panel.time_field
})
dashboard.refresh();
}
// I really don't like this function, too much dom manip. Break out into directive?
@ -234,7 +235,7 @@ angular.module('kibana.histogram', [])
$scope.modal = {
title: "Inspector",
body : "<h5>Last Elasticsearch Query</h5><pre>"+
'curl -XGET '+config.elasticsearch+'/'+$scope.index+"/_search?pretty -d'\n"+
'curl -XGET '+config.elasticsearch+'/'+dashboard.indices+"/_search?pretty -d'\n"+
angular.toJson(JSON.parse(request.toString()),true)+
"'</pre>",
}
@ -251,14 +252,8 @@ angular.module('kibana.histogram', [])
$scope.$emit('render');
}
$scope.set_time = function(time) {
$scope.time = time;
$scope.index = time.index || $scope.index
$scope.get_data();
}
})
.directive('histogramChart', function(eventBus) {
.directive('histogramChart', function(dashboard, eventBus, filterSrv, $rootScope) {
return {
restrict: 'A',
link: function(scope, elem, attrs, ctrl) {
@ -276,6 +271,14 @@ angular.module('kibana.histogram', [])
// Function for rendering panel
function render_panel() {
// Populate from the query service
try {
_.each(scope.data,function(series) {
series.label = series.info.alias,
series.color = series.info.color
})
} catch(e) {return}
// Set barwidth based on specified interval
var barwidth = interval_to_seconds(scope.panel.interval)*1000
@ -388,9 +391,13 @@ angular.module('kibana.histogram', [])
});
elem.bind("plotselected", function (event, ranges) {
scope.time.from = moment(ranges.xaxis.from);
scope.time.to = moment(ranges.xaxis.to)
eventBus.broadcast(scope.$id,scope.panel.group,'set_time',scope.time)
var _id = filterSrv.set({
type : 'time',
from : moment.utc(ranges.xaxis.from),
to : moment.utc(ranges.xaxis.to),
field : scope.panel.time_field
})
dashboard.refresh();
});
}
};

View File

@ -26,38 +26,4 @@
<label class="small">Labels</label><input type="checkbox" ng-model="panel.labels" ng-checked="panel.labels">
</div>
</div>
<h5>Queries</h5>
<div class="row-fluid">
<div class="span3">
<form style="margin-bottom: 0px">
<label class="small">Label</label>
<input type="text" placeholder="New Label" style="width:70%" ng-model="newlabel">
</form>
</div>
<div class="span8">
<form class="input-append" style="margin-bottom: 0px">
<label class="small">Query</label>
<input type="text" placeholder="New Query" style="width:80%" ng-model="newquery">
<button class="btn" ng-click="add_query(newlabel,newquery);newlabel='';newquery='';set_refresh(true)"><i class="icon-plus"></i></button>
</form>
</div>
<div class="span1">
</div>
</div>
<div class="row-fluid" ng-repeat="q in panel.query">
<div class="span3">
<form style="margin-bottom: 0px">
<input type="text" style="width:70%" ng-model="q.label" ng-change="set_refresh(true)">
</form>
</div>
<div class="span8">
<form class="input-append" style="margin-bottom: 0px">
<input type="text" style="width:80%" ng-model="q.query" ng-change="set_refresh(true)">
<button class="btn" ng-click="get_data()"><i class="icon-search"></i></button>
</form>
</div>
<div class="span1">
<i class="icon-remove pointer" ng-click="remove_query(q)"></i>
</div>
</div>
</div>

View File

@ -3,14 +3,14 @@
<div ng-show="panel.counter_pos == 'above' && (panel.chart == 'bar' || panel.chart == 'pie')" id='{{$id}}-legend'>
<!-- vertical legend -->
<table class="small" ng-show="panel.arrangement == 'vertical'">
<tr ng-repeat="query in plot.getData()">
<td><div style="display:inline-block;border-radius:5px;background:{{query.color}};height:10px;width:10px"></div></td> <td style="padding-right:10px;padding-left:10px;">{{query.label}}</td><td>{{query.data[0][1]}}</td>
<tr ng-repeat="query in data">
<td><div style="display:inline-block;border-radius:5px;background:{{query.info.color}};height:10px;width:10px"></div></td> <td style="padding-right:10px;padding-left:10px;">{{query.info.alias}}</td><td>{{query.data[0][1]}}</td>
</tr>
</table>
<!-- horizontal legend -->
<div class="small" ng-show="panel.arrangement == 'horizontal'" ng-repeat="query in plot.getData()" style="float:left;padding-left: 10px;">
<span><div style="display:inline-block;border-radius:5px;background:{{query.color}};height:10px;width:10px"></div></span> {{query.label}} ({{query.data[0][1]}}) </span>
<div class="small" ng-show="panel.arrangement == 'horizontal'" ng-repeat="query in data" style="float:left;padding-left: 10px;">
<span><div style="display:inline-block;border-radius:5px;background:{{query.info.color}};height:10px;width:10px"></div></span> {{query.info.alias}} ({{query.data[0][1]}}) </span>
</div><br>
</div>
@ -20,23 +20,26 @@
<div ng-show="panel.chart == 'pie' || panel.chart == 'bar'" hits-chart params="{{panel}}" style="height:{{panel.height || row.height}};position:relative"></div>
<div ng-show="panel.counter_pos == 'below' && (panel.chart == 'bar' || panel.chart == 'pie')" id='{{$id}}-legend'>
<!-- vertical legend -->
<table class="small" ng-show="panel.arrangement == 'vertical'">
<tr ng-repeat="query in plot.getData()">
<td><div style="display:inline-block;border-radius:5px;background:{{query.color}};height:10px;width:10px"></div></td> <td style="padding-right:10px;padding-left:10px;">{{query.label}}</td><td>{{query.data[0][1]}}</td>
<tr ng-repeat="query in data">
<td><div style="display:inline-block;border-radius:5px;background:{{query.info.color}};height:10px;width:10px"></div></td> <td style="padding-right:10px;padding-left:10px;">{{query.info.alias}}</td><td>{{query.data[0][1]}}</td>
</tr>
</table>
<!-- horizontal legend -->
<div class="small" ng-show="panel.arrangement == 'horizontal'" ng-repeat="query in plot.getData()" style="float:left;padding-left: 10px;">
<span><div style="display:inline-block;border-radius:5px;background:{{query.color}};height:10px;width:10px"></div></span> {{query.label}} ({{query.data[0][1]}}) </span>
<div class="small" ng-show="panel.arrangement == 'horizontal'" ng-repeat="query in data" style="float:left;padding-left: 10px;">
<span><div style="display:inline-block;border-radius:5px;background:{{query.info.color}};height:10px;width:10px"></div></span> {{query.info.alias}} ({{query.data[0][1]}}) </span>
</div><br>
</div>
<div ng-show="panel.chart == 'total'"><div ng-style="panel.style" style="line-height:{{panel.style['font-size']}}">{{hits}}</div></div>
<span ng-show="panel.chart == 'list'"><span ng-style="panel.style" style="line-height:{{panel.style['font-size']}}" ng-repeat="query in data">{{query.label}} ({{query.hits}})<span></span><br ng-show="panel.arrangement == 'vertical' && panel.chart == 'list'">
<span ng-show="panel.chart == 'list'">
<div ng-style="panel.style" style="display:inline-block;line-height:{{panel.style['font-size']}}" ng-repeat="query in data">
<i class="icon-circle" style="color:{{query.info.color}}"></i> {{query.info.alias}} ({{query.hits}})
</div>
</span><br ng-show="panel.arrangement == 'vertical' && panel.chart == 'list'">
</kibana-panel>

View File

@ -13,16 +13,10 @@
* donut :: Only applies to 'pie' charts. Punches a hole in the chart for some reason
* tilt :: Only 'pie' charts. Janky 3D effect. Looks terrible 90% of the time.
* lables :: Only 'pie' charts. Labels on the pie?
### Group Events
#### Sends
* get_time :: On panel initialization get time range to query
#### Receives
* time :: An object containing the time range to use and the index(es) to query
* query :: An Array of queries, even if its only one
*/
angular.module('kibana.hits', [])
.controller('hits', function($scope, eventBus) {
.controller('hits', function($scope, query, dashboard, filterSrv) {
// Set and populate defaults
var _d = {
@ -30,8 +24,8 @@ angular.module('kibana.hits', [])
query : ["*"],
group : "default",
style : { "font-size": '10pt'},
arrangement : 'vertical',
chart : 'none',
arrangement : 'horizontal',
chart : 'bar',
counter_pos : 'above',
donut : false,
tilt : false,
@ -41,17 +35,12 @@ angular.module('kibana.hits', [])
$scope.init = function () {
$scope.hits = 0;
eventBus.register($scope,'time', function(event,time){
set_time(time)
});
eventBus.register($scope,'query', function(event, query) {
$scope.panel.query = _.map(query,function(q) {
return {query: q, label: q};
$scope.$on('refresh',function(){
$scope.get_data();
})
$scope.get_data();
});
// Now that we're all setup, request the time from our group
eventBus.broadcast($scope.$id,$scope.panel.group,'get_time')
}
$scope.get_data = function(segment,query_id) {
@ -59,30 +48,24 @@ angular.module('kibana.hits', [])
$scope.panel.loading = true;
// Make sure we have everything for the request to complete
if(_.isUndefined($scope.index) || _.isUndefined($scope.time))
if(dashboard.indices.length == 0) {
return
}
var _segment = _.isUndefined(segment) ? 0 : segment
var request = $scope.ejs.Request().indices($scope.index[_segment]);
var request = $scope.ejs.Request().indices(dashboard.indices[_segment]);
// Build the question part of the query
var queries = [];
_.each($scope.panel.query, function(v) {
queries.push($scope.ejs.FilteredQuery(
ejs.QueryStringQuery(v.query || '*'),
ejs.RangeFilter($scope.time.field)
.from($scope.time.from)
.to($scope.time.to))
)
});
_.each(query.ids, function(id) {
var _q = $scope.ejs.FilteredQuery(
ejs.QueryStringQuery(query.list[id].query || '*'),
filterSrv.getBoolFilter(filterSrv.ids));
// Build the facet part
_.each(queries, function(v) {
request = request
.facet($scope.ejs.QueryFacet("query"+_.indexOf(queries,v))
.query(v)
.facet($scope.ejs.QueryFacet(id)
.query(_q)
).size(0)
})
});
// TODO: Spy for hits panel
//$scope.populate_modal(request);
@ -92,7 +75,6 @@ angular.module('kibana.hits', [])
// Populate scope when we have results
results.then(function(results) {
$scope.panel.loading = false;
if(_segment == 0) {
$scope.hits = 0;
@ -105,16 +87,25 @@ angular.module('kibana.hits', [])
$scope.panel.error = $scope.parse_error(results.error);
return;
}
if($scope.query_id === query_id) {
// Convert facet ids to numbers
var facetIds = _.map(_.keys(results.facets),function(k){return parseInt(k);})
// Make sure we're still on the same query/queries
if($scope.query_id === query_id &&
_.intersection(facetIds,query.ids).length == query.ids.length
) {
var i = 0;
_.each(results.facets, function(v, k) {
_.each(query.ids, function(id) {
var v = results.facets[id]
var hits = _.isUndefined($scope.data[i]) || _segment == 0 ?
v.count : $scope.data[i].hits+v.count
$scope.hits += v.count
// Create series
$scope.data[i] = {
label: $scope.panel.query[i].label || "query"+(parseInt(i)+1),
info: query.list[id],
id: id,
hits: hits,
data: [[i,hits]]
};
@ -122,26 +113,13 @@ angular.module('kibana.hits', [])
i++;
});
$scope.$emit('render');
if(_segment < $scope.index.length-1)
if(_segment < dashboard.indices.length-1)
$scope.get_data(_segment+1,query_id)
}
});
}
$scope.remove_query = function(q) {
$scope.panel.query = _.without($scope.panel.query,q);
$scope.get_data();
}
$scope.add_query = function(label,query) {
$scope.panel.query.unshift({
query: query,
label: label,
});
$scope.get_data();
}
$scope.set_refresh = function (state) {
$scope.refresh = state;
}
@ -155,11 +133,10 @@ angular.module('kibana.hits', [])
function set_time(time) {
$scope.time = time;
$scope.index = _.isUndefined(time.index) ? $scope.index : time.index
$scope.get_data();
}
}).directive('hitsChart', function(eventBus) {
}).directive('hitsChart', function(query) {
return {
restrict: 'A',
link: function(scope, elem, attrs, ctrl) {
@ -177,6 +154,13 @@ angular.module('kibana.hits', [])
// Function for rendering panel
function render_panel() {
try {
_.each(scope.data,function(series) {
series.label = series.info.alias,
series.color = series.info.color
})
} catch(e) {return}
var scripts = $LAB.script("common/lib/panels/jquery.flot.js").wait()
.script("common/lib/panels/jquery.flot.pie.js")
@ -196,13 +180,12 @@ angular.module('kibana.hits', [])
yaxis: { show: true, min: 0, color: "#c8c8c8" },
xaxis: { show: false },
grid: {
backgroundColor: '#272b30',
borderWidth: 0,
borderColor: '#eee',
color: "#eee",
hoverable: true,
},
colors: ['#86B22D','#BF6730','#1D7373','#BFB930','#BF3030','#77207D']
colors: query.colors
})
if(scope.panel.chart === 'pie')
scope.plot = $.plot(elem, scope.data, {
@ -218,7 +201,6 @@ angular.module('kibana.hits', [])
label: 'The Rest'
},
stroke: {
color: '#272b30',
width: 0
},
label: {
@ -234,7 +216,7 @@ angular.module('kibana.hits', [])
},
//grid: { hoverable: true, clickable: true },
grid: { hoverable: true, clickable: true },
colors: ['#86B22D','#BF6730','#1D7373','#BFB930','#BF3030','#77207D']
colors: query.colors
});
// Compensate for the height of the legend. Gross

View File

@ -11,13 +11,6 @@
<input bs-typeahead="fields.list" type="text" class="input-small" ng-model="panel.field">
</form>
</div>
<div class="span6">
<form class="input-append">
<h6>Query</h6>
<input type="text" ng-model="panel.query">
<button class="btn" ng-click="get_data();"><i class="icon-search"></i></button>
</form>
</div>
<div class="span1"><h6>Map</h6>
<select ng-change="$emit('render')" class="input-small" ng-model="panel.map" ng-options="f for f in ['world','europe','usa']"></select>
</div>

View File

@ -1,6 +1,66 @@
<kibana-panel ng-controller='map' ng-init="init()">
<span ng-show="panel.spyable" style="position:absolute;right:0px;top:0px" class='panelextra pointer'>
<style>
.jvectormap-label {
position: absolute;
display: none;
visibility: hidden;
border: solid 1px #CDCDCD;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
background: #292929;
color: white;
font-family: sans-serif, Verdana;
font-size: smaller;
padding: 3px;
}
.jvectormap-zoomin, .jvectormap-zoomout {
position: absolute;
left: 10px;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
background: #292929;
padding: 3px;
color: white;
width: 10px;
height: 10px;
cursor: pointer;
line-height: 10px;
text-align: center;
}
.jvectormap {
position: relative;
}
.jvectormap-zoomin {
display: none;
top: 10px;
}
.jvectormap-zoomout {
display: none;
top: 30px;
}
.map-legend {
color : #c8c8c8;
padding : 10px;
font-size: 11pt;
font-weight: 200;
background-color: #1f1f1f;
border-radius: 5px;
position: absolute;
right: 0px;
top: 15px;
display: none;
z-index: 99;
}
</style>
<span ng-show="panel.spyable" class='spy panelextra pointer'>
<i bs-modal="'partials/modal.html'" class="icon-eye-open"></i>
</span>
<div map params="{{panel}}" style="height:{{panel.height || row.height}}"></div>
<div class="jvectormap" map params="{{panel}}" style="height:{{panel.height || row.height}}"></div>
</kibana-panel>

View File

@ -19,16 +19,11 @@
* spyable :: Show the 'eye' icon that reveals the last ES query
* index_limit :: This does nothing yet. Eventually will limit the query to the first
N indices
### Group Events
#### Sends
* get_time :: On panel initialization get time range to query
#### Receives
* time :: An object containing the time range to use and the index(es) to query
* query :: An Array of queries, this panel uses only the first one
*/
angular.module('kibana.map', [])
.controller('map', function($scope, eventBus) {
.controller('map', function($scope, $rootScope, query, dashboard, filterSrv) {
// Set and populate defaults
var _d = {
@ -45,22 +40,24 @@ angular.module('kibana.map', [])
_.defaults($scope.panel,_d)
$scope.init = function() {
eventBus.register($scope,'time', function(event,time){set_time(time)});
eventBus.register($scope,'query', function(event, query) {
$scope.panel.query = _.isArray(query) ? query[0] : query;
$scope.$on('refresh',function(){$scope.get_data()})
$scope.get_data();
});
// Now that we're all setup, request the time from our group
eventBus.broadcast($scope.$id,$scope.panel.group,'get_time')
}
$scope.get_data = function() {
// Make sure we have everything for the request to complete
if(_.isUndefined($scope.index) || _.isUndefined($scope.time))
if(dashboard.indices.length == 0) {
return
}
$scope.panel.loading = true;
var request = $scope.ejs.Request().indices($scope.index);
var request = $scope.ejs.Request().indices(dashboard.indices);
var boolQuery = ejs.BoolQuery();
_.each(query.list,function(q) {
boolQuery = boolQuery.should(ejs.QueryStringQuery(q.query || '*'))
})
// Then the insert into facet and make the request
var request = request
@ -70,10 +67,8 @@ angular.module('kibana.map', [])
.exclude($scope.panel.exclude)
.facetFilter(ejs.QueryFilter(
ejs.FilteredQuery(
ejs.QueryStringQuery($scope.panel.query || '*'),
ejs.RangeFilter($scope.time.field)
.from($scope.time.from)
.to($scope.time.to)
boolQuery,
filterSrv.getBoolFilter(filterSrv.ids)
)))).size(0);
$scope.populate_modal(request);
@ -97,22 +92,15 @@ angular.module('kibana.map', [])
$scope.modal = {
title: "Inspector",
body : "<h5>Last Elasticsearch Query</h5><pre>"+
'curl -XGET '+config.elasticsearch+'/'+$scope.index+"/_search?pretty -d'\n"+
'curl -XGET '+config.elasticsearch+'/'+dashboard.indices+"/_search?pretty -d'\n"+
angular.toJson(JSON.parse(request.toString()),true)+
"'</pre>",
}
}
function set_time(time) {
$scope.time = time;
$scope.index = _.isUndefined(time.index) ? $scope.index : time.index
$scope.get_data();
}
$scope.build_search = function(field,value) {
$scope.panel.query = add_to_query($scope.panel.query,field,value,false)
$scope.get_data();
eventBus.broadcast($scope.$id,$scope.panel.group,'query',[$scope.panel.query]);
filterSrv.set({type:'querystring',mandate:'must',query:field+":"+value})
dashboard.refresh();
}
})
@ -155,20 +143,12 @@ angular.module('kibana.map', [])
}]
},
onRegionLabelShow: function(event, label, code){
$('.jvectormap-label').css({
"position" : "absolute",
"display" : "none",
'color' : "#c8c8c8",
'padding' : '10px',
'font-size' : '11pt',
'font-weight' : 200,
'background-color': '#1f1f1f',
'border-radius': '5px'
})
elem.children('.map-legend').show()
var count = _.isUndefined(scope.data[code]) ? 0 : scope.data[code];
$('.jvectormap-label').text(label.text() + ": " + count);
elem.children('.map-legend').text(label.text() + ": " + count);
},
onRegionOut: function(event, code) {
$('.map-legend').hide();
},
onRegionClick: function(event, code) {
var count = _.isUndefined(scope.data[code]) ? 0 : scope.data[code];
@ -176,6 +156,8 @@ angular.module('kibana.map', [])
scope.build_search(scope.panel.field,code)
}
});
elem.prepend('<span class="map-legend"></span>');
$('.map-legend').hide();
})
}
}

View File

@ -1,92 +0,0 @@
/**
* Hexagonal binning
* Rendered as normally projected svg paths, which mean they *do not*
* clip on spheres appropriately. To fix this, we would need to translate
* the svg path into a geo-path
*/
function displayBinning(scope, dr, dimensions) {
var hexbin = d3.hexbin()
.size(dimensions)
.radius(scope.panel.display.binning.hexagonSize);
var binPoints = [],
binnedPoints = [],
binRange = 0;
if (scope.panel.display.binning.enabled) {
/**
* primary field is just binning raw counts
*
* Secondary field is binning some metric like mean/median/total. Hexbins doesn't support that,
* so we cheat a little and just add more points to compensate.
* However, we don't want to add a million points, so normalize against the largest value
*/
if (scope.panel.display.binning.areaEncodingField === 'secondary') {
var max = Math.max.apply(Math, _.map(scope.data, function(k,v){return k;})),
scale = 50/max;
_.map(scope.data, function (k, v) {
var decoded = geohash.decode(v);
return _.map(_.range(0, k*scale), function(a,b) {
binPoints.push(dr.projection([decoded.longitude, decoded.latitude]));
})
});
} else {
binPoints = dr.projectedPoints;
}
//bin and sort the points, so we can set the various ranges appropriately
binnedPoints = hexbin(binPoints).sort(function(a, b) { return b.length - a.length; });
binRange = binnedPoints[0].length;
//clean up some memory
binPoints = [];
} else {
//not enabled, so just set an empty array. D3.exit will take care of the rest
binnedPoints = [];
binRange = 0;
}
var radius = d3.scale.sqrt()
.domain([0, binRange])
.range([0, scope.panel.display.binning.hexagonSize]);
var color = d3.scale.linear()
.domain([0,binRange])
.range(["white", "steelblue"])
.interpolate(d3.interpolateLab);
var hex = dr.g.selectAll(".hexagon")
.data(binnedPoints);
hex.enter().append("path")
.attr("d", function (d) {
if (scope.panel.display.binning.areaEncoding === false) {
return hexbin.hexagon();
} else {
return hexbin.hexagon(radius(d.length));
}
})
.attr("class", "hexagon")
.attr("transform", function (d) {
return "translate(" + d.x + "," + d.y + ")";
})
.style("fill", function (d) {
if (scope.panel.display.binning.colorEncoding === false) {
return color(binnedPoints[0].length / 2);
} else {
return color(d.length);
}
})
.attr("opacity", scope.panel.display.binning.hexagonAlpha);
hex.exit().remove();
}

View File

@ -1,28 +0,0 @@
/**
* Renders bullseyes as geo-json poly gon entities
* Allows for them to clip on spheres correctly
*/
function displayBullseye(scope, dr) {
var degrees = 180 / Math.PI
var circle = d3.geo.circle();
var data = [];
if (scope.panel.display.bullseye.enabled) {
data = [
circle.origin(parseFloat(scope.panel.display.bullseye.coord.lat), parseFloat(scope.panel.display.bullseye.coord.lon)).angle(1000 / 6371 * degrees)()
];
}
var arcs = dr.g.selectAll(".arc")
.data(data);
arcs.enter().append("path")
.attr("d", dr.path)
.attr("class", "arc");
arcs.exit().remove();
}

View File

@ -1,44 +0,0 @@
/**
* Renders geopoints as geo-json poly gon entities
* Allows for them to clip on spheres correctly
*/
function displayGeopoints(scope, dr) {
var points = [];
var circle = d3.geo.circle();
var degrees = 180 / Math.PI
if (scope.panel.display.geopoints.enabled) {
//points = dr.points;
points = _.map(dr.points, function(v) {
return {
type: "Point",
coordinates: [v[0], v[1]]
};
});
}
dr.geopoints = dr.g.selectAll("path.geopoint")
.data(points);
dr.geopoints.enter().append("path")
/*
.datum(function(d) {
return circle.origin([d[0], d[1]]).angle(scope.panel.display.geopoints.pointSize / 6371 * degrees)();
})
*/
.attr("class", "geopoint")
.attr("d", dr.path);
dr.geopoints.exit().remove();
}

View File

@ -1,287 +0,0 @@
<style>
.tabDetails {
border-bottom: 1px solid #ddd;
padding-bottom:20px;
}
.tabDetails td {
padding-right: 10px;
padding-bottom:10px;
}
</style>
<div ng-controller="map2">
<div class="row-fluid" style="margin-bottom:20px">
<div class="span11">
The map panel is compatible with either Geopoints or two-letter country codes, depending on the graphing options. Left click to drag/pan map, scroll wheel (or double click) to zoom. Globes can be spun using ctrl-key + drag.
</div>
</div>
<div class="row-fluid">
<div class="span10">
<form class="form-horizontal">
<div class="control-group">
<label class="control-label" for="panelfield">Primary Field</label>
<div class="controls">
<input type="text" id="panelfield" class="input"
ng-model="panel.field"
ng-change="get_data()" />
</div>
</div>
<div class="control-group">
<label class="control-label" for="panelsecondaryfield">Secondary Field</label>
<div class="controls">
<input type="text" id="panelsecondaryfield" class="input"
ng-model="panel.secondaryfield"
ng-change="get_data()"
data-placement="right"
placeholder="Optional"
bs-tooltip="'Allows aggregating on Primary field, while counting stats on a secondary (e.g. Group By user_id, Sum(purchase_price)).'" />
</div>
</div>
<div class="control-group">
<label class="control-label" for="panelquery">Query</label>
<div class="controls">
<input type="text" id="panelquery" class="input" ng-model="panel.query">
</div>
</div>
</form>
</div>
</div>
<div class="row-fluid">
<div class="span11">
<h4>Display Options</h4>
</div>
<!--
Rolling our own tab control here because the Angular-Strap Tab directive doesn't allow
updates to components inside, which is quite bizarre. Or I just can't figure it out...
-->
<div class="span11">
<ul class="nav nav-tabs" ng-cloak="">
<li ng-repeat="tab in ['Geopoints', 'Binning', 'Choropleth', 'Bullseye', 'Data']" ng-class="{active:isActive(tab)}">
<a ng-click="tabClick(tab)">{{tab}}</a>
</li>
</ul>
</div>
</div>
<div class="row-fluid tabDetails" ng-show="isActive('geopoints')">
<div class="span8 offset1">
<table>
<tbody >
<tr>
<td>Geopoints</td>
<td>
<button type="button" class="btn" bs-button
data-placement="right"
bs-tooltip="'Compatible with Geopoint Type'"
ng-change="$emit('render')"
ng-class="{'btn-success': panel.display.geopoints.enabled}"
ng-model="panel.display.geopoints.enabled">{{panel.display.geopoints.enabled|enabledText}}</button>
</td>
</tr>
<tr>
<td>Point size</td>
<td>
<input type="text" style="width:100px"
ng-change="$emit('render')"
data-placement="right"
bs-tooltip="'Controls the size of the geopoints on the map.'"
ng-model="panel.display.geopoints.pointSize"
value="{{panel.display.geopoints.pointSize}}" />
</td>
</tr>
<tr>
<td>Point Transparency</td>
<td>
<input type="text" style="width:100px"
ng-change="$emit('render')"
data-placement="right"
bs-tooltip="'Controls the transparency of geopoints. Valid numbers are between 0.0 and 1.0'"
ng-model="panel.display.geopoints.pointAlpha"
value="{{panel.display.geopoints.pointAlpha}}" />
</td>
</tr>
<tr>
<td>Autosizing</td>
<td>
<input type="checkbox"
ng-change="$emit('render')"
data-placement="right"
ng-model="panel.display.geopoints.autosize"
bs-tooltip="'Allows point sizes to scale as you zoom in and out of the map.'" />
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="row-fluid tabDetails" ng-show="isActive('binning')">
<div class="span8 offset1">
<table>
<tbody >
<tr>
<td>Binning</td>
<td>
<button type="button" class="btn" bs-button
data-placement="right"
bs-tooltip="'Compatible with Geopoint Type'"
ng-change="$emit('render')"
ng-class="{'btn-success': panel.display.binning.enabled}"
ng-model="panel.display.binning.enabled">{{panel.display.binning.enabled|enabledText}}</button>
</td>
</tr>
<tr>
<td>Hexagon size</td>
<td>
<input type="text" style="width:100px"
ng-change="$emit('render')"
data-placement="right"
bs-tooltip="'Controls the size of the hexagonal binning'"
ng-model="panel.display.binning.hexagonSize"
value="{{panel.display.binning.hexagonSize}}" />
</td>
</tr>
<tr>
<td>Hexagon Transparency</td>
<td>
<input type="text" style="width:100px"
ng-change="$emit('render')"
data-placement="right"
bs-tooltip="'Controls the transparency of hexagonal bins. Valid numbers are between 0.0 and 1.0'"
ng-model="panel.display.binning.hexagonAlpha"
value="{{panel.display.binning.hexagonAlpha}}" />
</td>
</tr>
<tr>
<td>
<button type="button" class="btn" bs-button
ng-change="$emit('render')"
ng-class="{'btn-success': panel.display.binning.areaEncoding}"
ng-model="panel.display.binning.areaEncoding">Area</button>
</td>
<td>
<div class="btn-group" ng-model="panel.display.binning.areaEncodingField" bs-buttons-radio ng-change="$emit('render')">
<button type="button" class="btn" value="primary">Primary Field</button>
<button type="button" class="btn" value="secondary">Secondary Field</button>
</div>
</td>
</tr>
<tr>
<td>
<button type="button" class="btn" bs-button
ng-change="$emit('render')"
ng-class="{'btn-success': panel.display.binning.colorEncoding}"
ng-model="panel.display.binning.colorEncoding">Color</button>
</td>
<td>
<div class="btn-group" ng-model="panel.display.binning.colorEncodingField" bs-buttons-radio ng-change="$emit('render')">
<button type="button" class="btn" value="primary">Primary Field</button>
<button type="button" class="btn" value="secondary">Secondary Field</button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="row-fluid tabDetails" ng-show="isActive('choropleth')">
<div class="span8 offset1">
<table>
<tbody >
<tr>
<td>Choropleth</td>
<td>
<button type="button" class="btn" bs-button
data-placement="right"
bs-tooltip="'Choropleths color country regions according to your selected field. Compatible with Country-Coded fields'"
ng-change="$emit('render')"
ng-class="{'btn-success': panel.display.choropleth.enabled}"
ng-model="panel.display.choropleth.enabled">{{panel.display.choropleth.enabled|enabledText}}</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="row-fluid tabDetails" ng-show="isActive('bullseye')">
<div class="span8 offset1">
<table>
<tbody >
<tr>
<td>Bullseye</td>
<td>
<button type="button" class="btn" bs-button
ng-change="$emit('render')"
ng-class="{'btn-success': panel.display.bullseye.enabled}"
ng-model="panel.display.bullseye.enabled">{{panel.display.bullseye.enabled|enabledText}}</button>
</td>
</tr>
<tr>
<td>Bullseye Coordinates</td>
<td>
<input type="text" style="width:100px"
ng-change="$emit('render')"
placeholder="Latitude"
data-placement="right"
bs-tooltip="'Latitude of Bullseye'"
ng-model="panel.display.bullseye.coord.lat"
value="{{panel.display.bullseye.coord.lat}}" />
<input type="text" style="width:100px"
placeholder="Longitude"
ng-change="$emit('render')"
data-placement="right"
bs-tooltip="'Longitude of Bullseye'"
ng-model="panel.display.bullseye.coord.lon"
value="{{panel.display.bullseye.coord.lon}}" />
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="row-fluid tabDetails" ng-show="isActive('data')">
<div class="span8 offset1">
<table>
<tbody >
<tr>
<td>Data Points</td>
<td>
<input type="text" style="width:100px"
ng-change="get_data()"
data-placement="right"
bs-tooltip="'Controls the number of samples used in the map. Be careful with this value!'"
ng-model="panel.display.data.samples"
value="{{panel.display.data.samples}}" />
</td>
</tr>
<tr>
<td>Map Projection</td>
<td>
<select ng-model="panel.display.data.type" ng-options="option.id as option.text for option in panel.display.data.dropdown"></select>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<h5>Panel Spy</h5>
<div class="row-fluid">
<div class="span2">
<label class="small">Spyable</label> <input type="checkbox" ng-model=
"panel.spyable" ng-checked="panel.spyable">
</div>
<div class="span9 small">
The panel spy shows 'behind the scenes' information about a panel. It can be
accessed by clicking the in the top right of the panel.
</div>
</div>
</div>

View File

@ -1 +0,0 @@
(function(){d3.hexbin=function(){function u(n){var r={};return n.forEach(function(n,t){var a=s.call(u,n,t)/o,e=Math.round(a),h=f.call(u,n,t)/i-(1&e?.5:0),c=Math.round(h),l=a-e;if(3*Math.abs(l)>1){var g=h-c,v=c+(c>h?-1:1)/2,M=e+(e>a?-1:1),m=h-v,d=a-M;g*g+l*l>m*m+d*d&&(c=v+(1&e?1:-1)/2,e=M)}var x=c+"-"+e,j=r[x];j?j.push(n):(j=r[x]=[n],j.x=(c+(1&e?.5:0))*i,j.y=e*o)}),d3.values(r)}function a(r){var t=0,u=0;return n.map(function(n){var a=Math.sin(n)*r,e=-Math.cos(n)*r,i=a-t,o=e-u;return t=a,u=e,[i,o]})}var e,i,o,h=1,c=1,f=r,s=t;return u.x=function(n){return arguments.length?(f=n,u):f},u.y=function(n){return arguments.length?(s=n,u):s},u.hexagon=function(n){return 1>arguments.length&&(n=e),"m"+a(n).join("l")+"z"},u.mesh=function(){for(var n=[],r=a(e).slice(0,4).join("l"),t=0,u=!1;c+e>t;t+=o,u=!u)for(var f=u?i/2:0;h>f;f+=i)n.push("M",f,",",t,"m",r);return n.join("")},u.size=function(n){return arguments.length?(h=+n[0],c=+n[1],u):[h,c]},u.radius=function(n){return arguments.length?(e=+n,i=2*e*Math.sin(Math.PI/3),o=1.5*e,u):e},u.radius(1)};var n=d3.range(0,2*Math.PI,Math.PI/3),r=function(n){return n[0]},t=function(n){return n[1]}})();

View File

@ -1,137 +0,0 @@
/**
* Copyright (c) 2011, Sun Ning.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
var BASE32_CODES = "0123456789bcdefghjkmnpqrstuvwxyz";
var BASE32_CODES_DICT = {};
for(var i=0; i<BASE32_CODES.length; i++) {
BASE32_CODES_DICT[BASE32_CODES.charAt(i)]=i;
}
var encode = function(latitude, longitude, numberOfChars){
numberOfChars = numberOfChars || 9;
var chars = [], bits = 0;
var hash_value = 0;
var maxlat = 90, minlat = -90;
var maxlon = 180, minlon = -180;
var mid;
var islon = true;
while(chars.length < numberOfChars) {
if (islon){
mid = (maxlon+minlon)/2;
if(longitude > mid){
hash_value = (hash_value << 1) + 1;
minlon=mid;
} else {
hash_value = (hash_value << 1) + 0;
maxlon=mid;
}
} else {
mid = (maxlat+minlat)/2;
if(latitude > mid ){
hash_value = (hash_value << 1) + 1;
minlat = mid;
} else {
hash_value = (hash_value << 1) + 0;
maxlat = mid;
}
}
islon = !islon;
bits++;
if (bits == 5) {
var code = BASE32_CODES[hash_value];
chars.push(code);
bits = 0;
hash_value = 0;
}
}
return chars.join('')
};
var decode_bbox = function(hash_string){
var islon = true;
var maxlat = 90, minlat = -90;
var maxlon = 180, minlon = -180;
var hash_value = 0;
for(var i=0,l=hash_string.length; i<l; i++) {
var code = hash_string[i].toLowerCase();
hash_value = BASE32_CODES_DICT[code];
for (var bits=4; bits>=0; bits--) {
var bit = (hash_value >> bits) & 1;
if (islon){
var mid = (maxlon+minlon)/2;
if(bit == 1){
minlon = mid;
} else {
maxlon = mid;
}
} else {
var mid = (maxlat+minlat)/2;
if(bit == 1){
minlat = mid;
} else {
maxlat = mid;
}
}
islon = !islon;
}
}
return [minlat, minlon, maxlat, maxlon];
}
var decode = function(hash_string){
var bbox = decode_bbox(hash_string);
var lat = (bbox[0]+bbox[2])/2;
var lon = (bbox[1]+bbox[3])/2;
var laterr = bbox[2]-lat;
var lonerr = bbox[3]-lon;
return {latitude:lat, longitude:lon,
error:{latitude:laterr, longitude:lonerr}};
};
/**
* direction [lat, lon], i.e.
* [1,0] - north
* [1,1] - northeast
* ...
*/
var neighbor = function(hashstring, direction) {
var lonlat = decode(hashstring);
var neighbor_lat = lonlat.latitude
+ direction[0] * lonlat.error.latitude * 2;
var neighbor_lon = lonlat.longitude
+ direction[1] * lonlat.error.longitude * 2;
return encode(neighbor_lat, neighbor_lon, hashstring.length);
}
var geohash = {
'encode': encode,
'decode': decode,
'decode_bbox': decode_bbox,
'neighbor': neighbor,
}
module.exports = geohash;

View File

@ -1 +0,0 @@
(function(){function n(n){function t(){for(;f=a<c.length&&n>p;){var u=a++,t=c[u],r=l.call(t,1);r.push(e(u)),++p,t[0].apply(null,r)}}function e(n){return function(u,l){--p,null==d&&(null!=u?(d=u,a=s=0/0,r()):(c[n]=l,--s?f||t():r()))}}function r(){null!=d?v(d):i?v(d,c):v.apply(null,[d].concat(c))}var o,f,i,c=[],a=0,p=0,s=0,d=null,v=u;return n||(n=1/0),o={defer:function(){return d||(c.push(arguments),++s,t()),o},await:function(n){return v=n,i=!1,s||r(),o},awaitAll:function(n){return v=n,i=!0,s||r(),o}}}function u(){}"undefined"==typeof module?self.queue=n:module.exports=n,n.version="1.0.4";var l=[].slice})();

View File

@ -1 +0,0 @@
topojson=function(){function t(t,e){function n(e){var n=t.arcs[e],r=n[0],o=[0,0];return n.forEach(function(t){o[0]+=t[0],o[1]+=t[1]}),[r,o]}var r={},o={},a={};e.forEach(function(t){var e=n(t);(r[e[0]]||(r[e[0]]=[])).push(t),(r[e[1]]||(r[e[1]]=[])).push(~t)}),e.forEach(function(t){var e,r,i=n(t),u=i[0],c=i[1];if(e=a[u])if(delete a[e.end],e.push(t),e.end=c,r=o[c]){delete o[r.start];var s=r===e?e:e.concat(r);o[s.start=e.start]=a[s.end=r.end]=s}else if(r=a[c]){delete o[r.start],delete a[r.end];var s=e.concat(r.map(function(t){return~t}).reverse());o[s.start=e.start]=a[s.end=r.start]=s}else o[e.start]=a[e.end]=e;else if(e=o[c])if(delete o[e.start],e.unshift(t),e.start=u,r=a[u]){delete a[r.end];var f=r===e?e:r.concat(e);o[f.start=r.start]=a[f.end=e.end]=f}else if(r=o[u]){delete o[r.start],delete a[r.end];var f=r.map(function(t){return~t}).reverse().concat(e);o[f.start=r.end]=a[f.end=e.end]=f}else o[e.start]=a[e.end]=e;else if(e=o[u])if(delete o[e.start],e.unshift(~t),e.start=c,r=a[c]){delete a[r.end];var f=r===e?e:r.concat(e);o[f.start=r.start]=a[f.end=e.end]=f}else if(r=o[c]){delete o[r.start],delete a[r.end];var f=r.map(function(t){return~t}).reverse().concat(e);o[f.start=r.end]=a[f.end=e.end]=f}else o[e.start]=a[e.end]=e;else if(e=a[c])if(delete a[e.end],e.push(~t),e.end=u,r=a[u]){delete o[r.start];var s=r===e?e:e.concat(r);o[s.start=e.start]=a[s.end=r.end]=s}else if(r=o[u]){delete o[r.start],delete a[r.end];var s=e.concat(r.map(function(t){return~t}).reverse());o[s.start=e.start]=a[s.end=r.start]=s}else o[e.start]=a[e.end]=e;else e=[t],o[e.start=u]=a[e.end=c]=e});var i=[];for(var u in a)i.push(a[u]);return i}function e(e,n,r){function a(t){0>t&&(t=~t),(l[t]||(l[t]=[])).push(f)}function i(t){t.forEach(a)}function u(t){t.forEach(i)}function c(t){t.type==="GeometryCollection"?t.geometries.forEach(c):t.type in d&&(f=t,d[t.type](t.arcs))}var s=[];if(arguments.length>1){var f,l=[],d={LineString:i,MultiLineString:u,Polygon:u,MultiPolygon:function(t){t.forEach(u)}};c(n),l.forEach(arguments.length<3?function(t,e){s.push([e])}:function(t,e){r(t[0],t[t.length-1])&&s.push([e])})}else for(var p=0,h=e.arcs.length;h>p;++p)s.push([p]);return o(e,{type:"MultiLineString",arcs:t(e,s)})}function n(t,e){return e.type==="GeometryCollection"?{type:"FeatureCollection",features:e.geometries.map(function(e){return r(t,e)})}:r(t,e)}function r(t,e){var n={type:"Feature",id:e.id,properties:e.properties||{},geometry:o(t,e)};return e.id==null&&delete n.id,n}function o(t,e){function n(t,e){e.length&&e.pop();for(var n,r=h[0>t?~t:t],o=0,i=r.length,u=0,c=0;i>o;++o)e.push([(u+=(n=r[o])[0])*f+d,(c+=n[1])*l+p]);0>t&&a(e,i)}function r(t){return[t[0]*f+d,t[1]*l+p]}function o(t){for(var e=[],r=0,o=t.length;o>r;++r)n(t[r],e);return e.length<2&&e.push(e[0]),e}function i(t){for(var e=o(t);e.length<4;)e.push(e[0]);return e}function u(t){return t.map(i)}function c(t){var e=t.type;return"GeometryCollection"===e?{type:e,geometries:t.geometries.map(c)}:e in v?{type:e,coordinates:v[e](t)}:null}var s=t.transform,f=s.scale[0],l=s.scale[1],d=s.translate[0],p=s.translate[1],h=t.arcs,v={Point:function(t){return r(t.coordinates)},MultiPoint:function(t){return t.coordinates.map(r)},LineString:function(t){return o(t.arcs)},MultiLineString:function(t){return t.arcs.map(o)},Polygon:function(t){return u(t.arcs)},MultiPolygon:function(t){return t.arcs.map(u)}};return c(e)}function a(t,e){for(var n,r=t.length,o=r-e;o<--r;)n=t[o],t[o++]=t[r],t[r]=n}function i(t,e){for(var n=0,r=t.length;r>n;){var o=n+r>>>1;t[o]<e?n=o+1:r=o}return n}function u(t){function e(t,e){t.forEach(function(t){0>t&&(t=~t);var n=o[t]||(o[t]=[]);n[e]||(n.forEach(function(t){var n,r;r=i(n=a[e],t),n[r]!==t&&n.splice(r,0,t),r=i(n=a[t],e),n[r]!==e&&n.splice(r,0,e)}),n[e]=e)})}function n(t,n){t.forEach(function(t){e(t,n)})}function r(t,e){t.type==="GeometryCollection"?t.geometries.forEach(function(t){r(t,e)}):t.type in u&&u[t.type](t.arcs,e)}var o=[],a=t.map(function(){return[]}),u={LineString:e,MultiLineString:n,Polygon:n,MultiPolygon:function(t,e){t.forEach(function(t){n(t,e)})}};return t.forEach(r),a}return{version:"1.0.0pre",mesh:e,feature:n,neighbors:u}}();

File diff suppressed because one or more lines are too long

View File

@ -1,251 +0,0 @@
id short name
4 AF Afghanistan
8 AL Albania
10 AQ Antarctica
12 DZ Algeria
16 AS American Samoa
20 AD Andorra
24 AO Angola
28 AG Antigua and Barbuda
31 AZ Azerbaijan
32 AR Argentina
36 AU Australia
40 AT Austria
44 BS Bahamas
48 BH Bahrain
50 BD Bangladesh
51 AM Armenia
52 BB Barbados
56 BE Belgium
60 BM Bermuda
64 BT Bhutan
68 BO Bolivia, Plurinational State of
70 BA Bosnia and Herzegovina
72 BW Botswana
74 BV Bouvet Island
76 BR Brazil
84 BZ Belize
86 IO British Indian Ocean Territory
90 SB Solomon Islands
92 VG Virgin Islands, British
96 BN Brunei Darussalam
100 BG Bulgaria
104 MM Myanmar
108 BI Burundi
112 BY Belarus
116 KH Cambodia
120 CM Cameroon
124 CA Canada
132 CV Cape Verde
136 KY Cayman Islands
140 CF Central African Republic
144 LK Sri Lanka
148 TD Chad
152 CL Chile
156 CN China
158 TW Taiwan, Province of China
162 CX Christmas Island
166 CC Cocos (Keeling) Islands
170 CO Colombia
174 KM Comoros
175 YT Mayotte
178 CG Congo
180 CD Congo, the Democratic Republic of the
184 CK Cook Islands
188 CR Costa Rica
191 HR Croatia
192 CU Cuba
196 CY Cyprus
203 CZ Czech Republic
204 BJ Benin
208 DK Denmark
212 DM Dominica
214 DO Dominican Republic
218 EC Ecuador
222 SV El Salvador
226 GQ Equatorial Guinea
231 ET Ethiopia
232 ER Eritrea
233 EE Estonia
234 FO Faroe Islands
238 FK Falkland Islands (Malvinas)
239 GS South Georgia and the South Sandwich Islands
242 FJ Fiji
246 FI Finland
248 AX Åland Islands
250 FR France
254 GF French Guiana
258 PF French Polynesia
260 TF French Southern Territories
262 DJ Djibouti
266 GA Gabon
268 GE Georgia
270 GM Gambia
275 Palestinian Territory, Occupied
276 DE Germany
288 GH Ghana
292 GI Gibraltar
296 KI Kiribati
300 GR Greece
304 GL Greenland
308 GD Grenada
312 GP Guadeloupe
316 GU Guam
320 GT Guatemala
324 GN Guinea
328 GY Guyana
332 HT Haiti
334 HM Heard Island and McDonald Islands
336 VA Holy See (Vatican City State)
340 HN Honduras
344 HK Hong Kong
348 HU Hungary
352 IS Iceland
356 IN India
360 ID Indonesia
364 IR Iran, Islamic Republic of
368 IQ Iraq
372 IE Ireland
376 IL Israel
380 IT Italy
384 Côte d'Ivoire
388 JM Jamaica
392 JP Japan
398 KZ Kazakhstan
400 JO Jordan
404 KE Kenya
408 KP Korea, Democratic People's Republic of
410 KR Korea, Republic of
414 KW Kuwait
417 KG Kyrgyzstan
418 LA Lao People's Democratic Republic
422 LB Lebanon
426 LS Lesotho
428 LV Latvia
430 LR Liberia
434 LY Libya
438 LI Liechtenstein
440 LT Lithuania
442 LU Luxembourg
446 MO Macao
450 MG Madagascar
454 MW Malawi
458 MY Malaysia
462 MV Maldives
466 ML Mali
470 MT Malta
474 MQ Martinique
478 MR Mauritania
480 MU Mauritius
484 MX Mexico
492 MC Monaco
496 MN Mongolia
498 MD Moldova, Republic of
499 ME Montenegro
500 MS Montserrat
504 MA Morocco
508 MZ Mozambique
512 OM Oman
516 NA Namibia
520 NR Nauru
524 NP Nepal
528 NL Netherlands
531 Curaçao
533 AW Aruba
534 SX Sint Maarten (Dutch part)
535 BQ Bonaire, Sint Eustatius and Saba
540 NC New Caledonia
548 VU Vanuatu
554 NZ New Zealand
558 NI Nicaragua
562 NE Niger
566 NG Nigeria
570 NU Niue
574 NF Norfolk Island
578 NO Norway
580 MP Northern Mariana Islands
581 UM United States Minor Outlying Islands
583 FM Micronesia, Federated States of
584 MH Marshall Islands
585 PW Palau
586 PK Pakistan
591 PA Panama
598 PG Papua New Guinea
600 PY Paraguay
604 PE Peru
608 PH Philippines
612 PN Pitcairn
616 PL Poland
620 PT Portugal
624 GW Guinea-Bissau
626 TL Timor-Leste
630 PR Puerto Rico
634 QA Qatar
638 Réunion
642 RO Romania
643 RU Russian Federation
646 RW Rwanda
652 Saint Barthélemy
654 SH Saint Helena, Ascension and Tristan da Cunha
659 KN Saint Kitts and Nevis
660 AI Anguilla
662 LC Saint Lucia
663 MF Saint Martin (French part)
666 PM Saint Pierre and Miquelon
670 VC Saint Vincent and the Grenadines
674 SM San Marino
678 ST Sao Tome and Principe
682 SA Saudi Arabia
686 SN Senegal
688 RS Serbia
690 SC Seychelles
694 SL Sierra Leone
702 SG Singapore
703 SK Slovakia
704 VN Viet Nam
705 SI Slovenia
706 SO Somalia
710 ZA South Africa
716 ZW Zimbabwe
724 ES Spain
728 SS South Sudan
729 SD Sudan
732 EH Western Sahara
740 SR Suriname
744 SJ Svalbard and Jan Mayen
748 SZ Swaziland
752 SE Sweden
756 CH Switzerland
760 SY Syrian Arab Republic
762 TJ Tajikistan
764 TH Thailand
768 TG Togo
772 TK Tokelau
776 TO Tonga
780 TT Trinidad and Tobago
784 AE United Arab Emirates
788 TN Tunisia
792 TR Turkey
795 TM Turkmenistan
796 TC Turks and Caicos Islands
798 TV Tuvalu
800 UG Uganda
804 UA Ukraine
807 MK Macedonia, the former Yugoslav Republic of
818 EG Egypt
826 GB United Kingdom
831 GG Guernsey
832 JE Jersey
833 IM Isle of Man
834 TZ Tanzania, United Republic of
840 US United States
850 VI Virgin Islands, U.S.
854 BF Burkina Faso
858 UY Uruguay
860 UZ Uzbekistan
862 VE Venezuela, Bolivarian Republic of
876 WF Wallis and Futuna
882 WS Samoa
887 YE Yemen
894 ZM Zambia
1 id short name
2 4 AF Afghanistan
3 8 AL Albania
4 10 AQ Antarctica
5 12 DZ Algeria
6 16 AS American Samoa
7 20 AD Andorra
8 24 AO Angola
9 28 AG Antigua and Barbuda
10 31 AZ Azerbaijan
11 32 AR Argentina
12 36 AU Australia
13 40 AT Austria
14 44 BS Bahamas
15 48 BH Bahrain
16 50 BD Bangladesh
17 51 AM Armenia
18 52 BB Barbados
19 56 BE Belgium
20 60 BM Bermuda
21 64 BT Bhutan
22 68 BO Bolivia, Plurinational State of
23 70 BA Bosnia and Herzegovina
24 72 BW Botswana
25 74 BV Bouvet Island
26 76 BR Brazil
27 84 BZ Belize
28 86 IO British Indian Ocean Territory
29 90 SB Solomon Islands
30 92 VG Virgin Islands, British
31 96 BN Brunei Darussalam
32 100 BG Bulgaria
33 104 MM Myanmar
34 108 BI Burundi
35 112 BY Belarus
36 116 KH Cambodia
37 120 CM Cameroon
38 124 CA Canada
39 132 CV Cape Verde
40 136 KY Cayman Islands
41 140 CF Central African Republic
42 144 LK Sri Lanka
43 148 TD Chad
44 152 CL Chile
45 156 CN China
46 158 TW Taiwan, Province of China
47 162 CX Christmas Island
48 166 CC Cocos (Keeling) Islands
49 170 CO Colombia
50 174 KM Comoros
51 175 YT Mayotte
52 178 CG Congo
53 180 CD Congo, the Democratic Republic of the
54 184 CK Cook Islands
55 188 CR Costa Rica
56 191 HR Croatia
57 192 CU Cuba
58 196 CY Cyprus
59 203 CZ Czech Republic
60 204 BJ Benin
61 208 DK Denmark
62 212 DM Dominica
63 214 DO Dominican Republic
64 218 EC Ecuador
65 222 SV El Salvador
66 226 GQ Equatorial Guinea
67 231 ET Ethiopia
68 232 ER Eritrea
69 233 EE Estonia
70 234 FO Faroe Islands
71 238 FK Falkland Islands (Malvinas)
72 239 GS South Georgia and the South Sandwich Islands
73 242 FJ Fiji
74 246 FI Finland
75 248 AX Åland Islands
76 250 FR France
77 254 GF French Guiana
78 258 PF French Polynesia
79 260 TF French Southern Territories
80 262 DJ Djibouti
81 266 GA Gabon
82 268 GE Georgia
83 270 GM Gambia
84 275 Palestinian Territory, Occupied
85 276 DE Germany
86 288 GH Ghana
87 292 GI Gibraltar
88 296 KI Kiribati
89 300 GR Greece
90 304 GL Greenland
91 308 GD Grenada
92 312 GP Guadeloupe
93 316 GU Guam
94 320 GT Guatemala
95 324 GN Guinea
96 328 GY Guyana
97 332 HT Haiti
98 334 HM Heard Island and McDonald Islands
99 336 VA Holy See (Vatican City State)
100 340 HN Honduras
101 344 HK Hong Kong
102 348 HU Hungary
103 352 IS Iceland
104 356 IN India
105 360 ID Indonesia
106 364 IR Iran, Islamic Republic of
107 368 IQ Iraq
108 372 IE Ireland
109 376 IL Israel
110 380 IT Italy
111 384 Côte d'Ivoire
112 388 JM Jamaica
113 392 JP Japan
114 398 KZ Kazakhstan
115 400 JO Jordan
116 404 KE Kenya
117 408 KP Korea, Democratic People's Republic of
118 410 KR Korea, Republic of
119 414 KW Kuwait
120 417 KG Kyrgyzstan
121 418 LA Lao People's Democratic Republic
122 422 LB Lebanon
123 426 LS Lesotho
124 428 LV Latvia
125 430 LR Liberia
126 434 LY Libya
127 438 LI Liechtenstein
128 440 LT Lithuania
129 442 LU Luxembourg
130 446 MO Macao
131 450 MG Madagascar
132 454 MW Malawi
133 458 MY Malaysia
134 462 MV Maldives
135 466 ML Mali
136 470 MT Malta
137 474 MQ Martinique
138 478 MR Mauritania
139 480 MU Mauritius
140 484 MX Mexico
141 492 MC Monaco
142 496 MN Mongolia
143 498 MD Moldova, Republic of
144 499 ME Montenegro
145 500 MS Montserrat
146 504 MA Morocco
147 508 MZ Mozambique
148 512 OM Oman
149 516 NA Namibia
150 520 NR Nauru
151 524 NP Nepal
152 528 NL Netherlands
153 531 Curaçao
154 533 AW Aruba
155 534 SX Sint Maarten (Dutch part)
156 535 BQ Bonaire, Sint Eustatius and Saba
157 540 NC New Caledonia
158 548 VU Vanuatu
159 554 NZ New Zealand
160 558 NI Nicaragua
161 562 NE Niger
162 566 NG Nigeria
163 570 NU Niue
164 574 NF Norfolk Island
165 578 NO Norway
166 580 MP Northern Mariana Islands
167 581 UM United States Minor Outlying Islands
168 583 FM Micronesia, Federated States of
169 584 MH Marshall Islands
170 585 PW Palau
171 586 PK Pakistan
172 591 PA Panama
173 598 PG Papua New Guinea
174 600 PY Paraguay
175 604 PE Peru
176 608 PH Philippines
177 612 PN Pitcairn
178 616 PL Poland
179 620 PT Portugal
180 624 GW Guinea-Bissau
181 626 TL Timor-Leste
182 630 PR Puerto Rico
183 634 QA Qatar
184 638 Réunion
185 642 RO Romania
186 643 RU Russian Federation
187 646 RW Rwanda
188 652 Saint Barthélemy
189 654 SH Saint Helena, Ascension and Tristan da Cunha
190 659 KN Saint Kitts and Nevis
191 660 AI Anguilla
192 662 LC Saint Lucia
193 663 MF Saint Martin (French part)
194 666 PM Saint Pierre and Miquelon
195 670 VC Saint Vincent and the Grenadines
196 674 SM San Marino
197 678 ST Sao Tome and Principe
198 682 SA Saudi Arabia
199 686 SN Senegal
200 688 RS Serbia
201 690 SC Seychelles
202 694 SL Sierra Leone
203 702 SG Singapore
204 703 SK Slovakia
205 704 VN Viet Nam
206 705 SI Slovenia
207 706 SO Somalia
208 710 ZA South Africa
209 716 ZW Zimbabwe
210 724 ES Spain
211 728 SS South Sudan
212 729 SD Sudan
213 732 EH Western Sahara
214 740 SR Suriname
215 744 SJ Svalbard and Jan Mayen
216 748 SZ Swaziland
217 752 SE Sweden
218 756 CH Switzerland
219 760 SY Syrian Arab Republic
220 762 TJ Tajikistan
221 764 TH Thailand
222 768 TG Togo
223 772 TK Tokelau
224 776 TO Tonga
225 780 TT Trinidad and Tobago
226 784 AE United Arab Emirates
227 788 TN Tunisia
228 792 TR Turkey
229 795 TM Turkmenistan
230 796 TC Turks and Caicos Islands
231 798 TV Tuvalu
232 800 UG Uganda
233 804 UA Ukraine
234 807 MK Macedonia, the former Yugoslav Republic of
235 818 EG Egypt
236 826 GB United Kingdom
237 831 GG Guernsey
238 832 JE Jersey
239 833 IM Isle of Man
240 834 TZ Tanzania, United Republic of
241 840 US United States
242 850 VI Virgin Islands, U.S.
243 854 BF Burkina Faso
244 858 UY Uruguay
245 860 UZ Uzbekistan
246 862 VE Venezuela, Bolivarian Republic of
247 876 WF Wallis and Futuna
248 882 WS Samoa
249 887 YE Yemen
250 894 ZM Zambia
251

View File

@ -1,58 +0,0 @@
<kibana-panel ng-controller='map2' ng-init="init()">
<style>
.overlay {
fill: none;
pointer-events: all;
}
.land {
fill: #D1D1D1;
stroke: #595959;
stroke-linejoin: round;
stroke-linecap: round;
stroke-width: .1px;
}
.boundary {
fill: none;
stroke: #000;
stroke-linejoin: round;
stroke-linecap: round;
}
.hexagon {
fill: none;
stroke: #000;
stroke-width: .1px;
}
.q1 { fill:rgb(247,251,255); }
.q2 { fill:rgb(222,235,247); }
.q3 { fill:rgb(198,219,239); }
.q4 { fill:rgb(158,202,225); }
.q5 { fill:rgb(107,174,214); }
.q6 { fill:rgb(66,146,198); }
.q7 { fill:rgb(33,113,181); }
.q8 { fill:rgb(8,81,156); }
.q9 { fill:rgb(8,48,107); }
.arc {
stroke: #f00;
stroke-width: .5px;
fill: none;
}
.geopoint {
stroke: #000;
stroke-width: .5px;
fill: #000;
}
.dropdown-menu{position:absolute;top:auto;left:auto;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;background-color:#ffffff;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.2);*border-right-width:2px;*border-bottom-width:2px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box;}
</style>
<span ng-show="panel.spyable" style="position:absolute;right:0px;top:0px" class='panelextra pointer'>
<i bs-modal="'partials/modal.html'" class="icon-eye-open"></i>
</span>
<div map2 params="{{panel}}" style="height:{{panel.height || row.height}}"></div>
</kibana-panel>

View File

@ -1,452 +0,0 @@
angular.module('kibana.map2', [])
.controller('map2', function ($scope, eventBus, keylistener) {
// Set and populate defaults
var _d = {
status : "Broken",
query: "*",
map: "world",
colors: ['#C8EEFF', '#0071A4'],
size: 100,
exclude: [],
spyable: true,
group: "default",
index_limit: 0,
display: {
translate:[0, 0],
scale:-1,
data: {
samples: 1000,
type: "mercator",
dropdown:[
{
"text": "Mercator (Flat)",
id: "mercator"
},
{
text: "Orthographic (Sphere)",
id: "orthographic"
}
]
},
geopoints: {
enabled: false,
enabledText: "Enabled",
pointSize: 0.3,
pointAlpha: 0.6
},
binning: {
enabled: false,
hexagonSize: 2,
hexagonAlpha: 1.0,
areaEncoding: true,
areaEncodingField: "primary",
colorEncoding: true,
colorEncodingField: "primary"
},
choropleth: {
enabled: false
},
bullseye: {
enabled: false,
coord: {
lat: 0,
lon: 0
}
}
},
activeDisplayTab:"Geopoints"
};
_.defaults($scope.panel, _d)
$scope.init = function () {
eventBus.register($scope, 'time', function (event, time) {
set_time(time)
});
eventBus.register($scope, 'query', function (event, query) {
$scope.panel.query = _.isArray(query) ? query[0] : query;
$scope.get_data();
});
// Now that we're all setup, request the time from our group
eventBus.broadcast($scope.$id, $scope.panel.group, 'get_time');
$scope.keylistener = keylistener;
};
$scope.get_data = function () {
// Make sure we have everything for the request to complete
if (_.isUndefined($scope.index) || _.isUndefined($scope.time))
return
$scope.panel.loading = true;
var request = $scope.ejs.Request().indices($scope.index);
var metric = 'count';
//Use a regular term facet if there is no secondary field
if (typeof $scope.panel.secondaryfield === "undefined") {
var facet = $scope.ejs.TermsFacet('map')
.field($scope.panel.field)
.size($scope.panel.display.data.samples)
.exclude($scope.panel.exclude)
.facetFilter(ejs.QueryFilter(
ejs.FilteredQuery(
ejs.QueryStringQuery($scope.panel.query || '*'),
ejs.RangeFilter($scope.time.field)
.from($scope.time.from)
.to($scope.time.to))));
metric = 'count';
} else {
//otherwise, use term stats
//NOTE: this will break if valueField is a geo_point
// need to put in checks for that
var facet = $scope.ejs.TermStatsFacet('map')
.keyField($scope.panel.field)
.valueField($scope.panel.secondaryfield)
.size($scope.panel.display.data.samples)
.facetFilter(ejs.QueryFilter(
ejs.FilteredQuery(
ejs.QueryStringQuery($scope.panel.query || '*'),
ejs.RangeFilter($scope.time.field)
.from($scope.time.from)
.to($scope.time.to))));
metric = 'total';
}
// Then the insert into facet and make the request
var request = request.facet(facet).size(0);
$scope.populate_modal(request);
var results = request.doSearch();
// Populate scope when we have results
results.then(function (results) {
$scope.panel.loading = false;
$scope.hits = results.hits.total;
$scope.data = {};
_.each(results.facets.map.terms, function (v) {
if (!_.isNumber(v.term)) {
$scope.data[v.term.toUpperCase()] = v[metric];
} else {
$scope.data[v.term] = v[metric];
}
});
$scope.$emit('render')
});
};
// I really don't like this function, too much dom manip. Break out into directive?
$scope.populate_modal = function (request) {
$scope.modal = {
title: "Inspector",
body: "<h5>Last Elasticsearch Query</h5><pre>" + 'curl -XGET ' + config.elasticsearch + '/' + $scope.index + "/_search?pretty -d'\n" + angular.toJson(JSON.parse(request.toString()), true) + "'</pre>"
}
};
function set_time(time) {
$scope.time = time;
$scope.index = _.isUndefined(time.index) ? $scope.index : time.index
$scope.get_data();
}
$scope.build_search = function (field, value) {
$scope.panel.query = add_to_query($scope.panel.query, field, value, false)
$scope.get_data();
eventBus.broadcast($scope.$id, $scope.panel.group, 'query', $scope.panel.query);
};
$scope.isActive = function(tab) {
return (tab.toLowerCase() === $scope.panel.activeDisplayTab.toLowerCase());
}
$scope.tabClick = function(tab) {
$scope.panel.activeDisplayTab = tab;
}
})
.filter('enabledText', function() {
return function (value) {
if (value === true) {
return "Enabled";
} else {
return "Disabled";
}
}
})
.directive('map2', function () {
return {
restrict: 'A',
link: function (scope, elem, attrs) {
//directive level variables related to d3
var dr = {};
scope.initializing = false;
dr.worldData = null;
dr.worldNames = null;
/**
* Initialize the panels if new, or render existing panels
*/
scope.init_or_render = function() {
if (typeof dr.svg === 'undefined') {
console.log("init");
//prevent duplicate initialization steps, if render is called again
//before the svg is setup
if (!scope.initializing) {
init_panel();
}
} else {
console.log("render");
render_panel();
}
};
/**
* Receive render events
*/
scope.$on('render', function () {
scope.init_or_render();
});
/**
* On window resize, re-render the panel
*/
angular.element(window).bind('resize', function () {
scope.init_or_render();
});
/**
* Load the various panel-specific scripts, map data, then initialize
* the svg and set appropriate D3 settings
*/
function init_panel() {
scope.initializing = true;
// Using LABjs, wait until all scripts are loaded before rendering panel
var scripts = $LAB.script("common/lib/d3.v3.min.js?rand="+Math.floor(Math.random()*10000))
.script("panels/map2/lib/topojson.v1.min.js?rand="+Math.floor(Math.random()*10000))
.script("panels/map2/lib/node-geohash.js?rand="+Math.floor(Math.random()*10000))
.script("panels/map2/lib/d3.hexbin.v0.min.js?rand="+Math.floor(Math.random()*10000))
.script("panels/map2/lib/queue.v1.min.js?rand="+Math.floor(Math.random()*10000))
.script("panels/map2/display/binning.js?rand="+Math.floor(Math.random()*10000))
.script("panels/map2/display/geopoints.js?rand="+Math.floor(Math.random()*10000))
.script("panels/map2/display/bullseye.js?rand="+Math.floor(Math.random()*10000));
// Populate element. Note that jvectormap appends, does not replace.
scripts.wait(function () {
queue()
.defer(d3.json, "panels/map2/lib/world-110m.json")
.defer(d3.tsv, "panels/map2/lib/world-country-names.tsv")
.await(function(error, world, names) {
dr.worldData = world;
dr.worldNames = names;
//Better way to get these values? Seems kludgy to use jQuery on the div...
var width = $(elem[0]).width(),
height = $(elem[0]).height();
//scale to whichever dimension is smaller, helps to ensure the whole map is displayed
dr.scale = (width > height) ? (height/5) : (width/5);
dr.zoom = d3.behavior.zoom()
.scaleExtent([1, 20])
.on("zoom", translate_map);
//used by choropleth
//@todo change domain so that it reflects the domain of the data
dr.quantize = d3.scale.quantize()
.domain([0, 1000])
.range(d3.range(9).map(function(i) { return "q" + (i+1); }));
//Extract name and two-letter codes for our countries
dr.countries = topojson.feature(dr.worldData, dr.worldData.objects.countries).features;
dr.countries = dr.countries.filter(function(d) {
return dr.worldNames.some(function(n) {
if (d.id == n.id) {
d.name = n.name;
return d.short = n.short;
}
});
}).sort(function(a, b) {
return a.name.localeCompare(b.name);
});
//create the new svg
dr.svg = d3.select(elem[0]).append("svg")
.attr("width", "100%")
.attr("height", "100%")
.attr("viewBox", "0 0 " + width + " " + height)
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")
.call(dr.zoom);
dr.g = dr.svg.append("g");
scope.initializing = false;
render_panel();
});
});
}
/**
* Render updates to the SVG. Typically happens when the data changes (time, query)
* or when new options are selected
*/
function render_panel() {
var width = $(elem[0]).width(),
height = $(elem[0]).height();
//Projection is dependant on the map-type
if (scope.panel.display.data.type === 'mercator') {
dr.projection = d3.geo.mercator()
.translate([width/2, height/2])
.scale(dr.scale);
} else if (scope.panel.display.data.type === 'orthographic') {
dr.projection = d3.geo.orthographic()
.translate([width/2, height/2])
.scale(100)
.clipAngle(90);
//recenters the sphere more towards the US...not really necessary
dr.projection.rotate([100 / 2, 20 / 2, dr.projection.rotate()[2]]);
}
dr.path = d3.geo.path()
.projection(dr.projection).pointRadius(0.2);
console.log(scope.data);
//Geocoded points are decoded into lonlat
dr.points = _.map(scope.data, function (k, v) {
//console.log(k,v);
var decoded = geohash.decode(v);
return [decoded.longitude, decoded.latitude];
});
//And also projected projected to x/y. Both sets of points are used
//by different functions
dr.projectedPoints = _.map(dr.points, function (coords) {
return dr.projection(coords);
});
dr.svg.select(".overlay").remove();
dr.svg.append("rect")
.attr("class", "overlay")
.attr("width", width)
.attr("height", height)
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
//Draw the countries, if this is a choropleth, draw with fancy colors
var countryPath = dr.g.selectAll(".land")
.data(dr.countries);
countryPath.enter().append("path")
.attr("class", function(d) {
if (scope.panel.display.choropleth.enabled) {
return 'land ' + dr.quantize(scope.data[d.short]);
} else {
return 'land';
}
})
.attr("d", dr.path);
countryPath.exit().remove();
//If this is a sphere, set up drag and keypress listeners
if (scope.panel.display.data.type === 'orthographic') {
dr.svg.style("cursor", "move")
.call(d3.behavior.drag()
.origin(function() { var rotate = dr.projection.rotate(); return {x: 2 * rotate[0], y: -2 * rotate[1]}; })
.on("drag", function() {
if (scope.keylistener.keyActive(17)) {
dr.projection.rotate([d3.event.x / 2, -d3.event.y / 2, dr.projection.rotate()[2]]);
//dr.svg.selectAll("path").attr("d", dr.path);
dr.g.selectAll("path").attr("d", dr.path);
}
}));
}
//Special fix for when the user changes from mercator -> orthographic
//The globe won't redraw automatically, we need to force it
if (scope.panel.display.data.type === 'orthographic') {
dr.svg.selectAll("path").attr("d", dr.path);
}
/**
* Display option rendering
* Order is important to render order here!
*/
//@todo fix this
var dimensions = [width, height];
displayBinning(scope, dr, dimensions);
displayGeopoints(scope, dr);
displayBullseye(scope, dr);
//If the panel scale is not default (e.g. the user has moved the maps around)
//set the scale and position to the last saved config
if (scope.panel.display.scale != -1) {
dr.zoom.scale(scope.panel.display.scale).translate(scope.panel.display.translate);
dr.g.style("stroke-width", 1 / scope.panel.display.scale).attr("transform", "translate(" + scope.panel.display.translate + ") scale(" + scope.panel.display.scale + ")");
}
}
/**
* On D3 zoom events, pan/zoom the map
* Only applies if the ctrl-key is not pressed, so it doesn't clobber
* sphere dragging
*/
function translate_map() {
var width = $(elem[0]).width(),
height = $(elem[0]).height();
if (! scope.keylistener.keyActive(17)) {
var t = d3.event.translate,
s = d3.event.scale;
t[0] = Math.min(width / 2 * (s - 1), Math.max(width / 2 * (1 - s), t[0]));
t[1] = Math.min(height / 2 * (s - 1) + 230 * s, Math.max(height / 2 * (1 - s) - 230 * s, t[1]));
dr.zoom.translate(t);
scope.panel.display.translate = t;
scope.panel.display.scale = s;
dr.g.style("stroke-width", 1 / s).attr("transform", "translate(" + t + ") scale(" + s + ")");
}
}
}
};
});

View File

@ -1,21 +0,0 @@
<div class="row-fluid" ng-controller="parallelcoordinates">
<div style="width:90%">
<form class="input-append">
<h6>Query</h6>
<input type="text" style="width:90%" ng-model="panel.query">
<button class="btn" ng-click="get_data();"><i class="icon-search"></i></button>
</form>
</div>
</div>
<h5>Panel Spy</h5>
<div class="row-fluid">
<div class="span2">
<label class="small"> Spyable </label><input type="checkbox" ng-model="panel.spyable" ng-checked="panel.spyable">
</div>
<div class="span9 small">
The panel spy shows 'behind the scenes' information about a panel. It can
be accessed by clicking the <i class='icon-eye-open'></i> in the top right
of the panel.
</div>
</div>

View File

@ -1,42 +0,0 @@
<style>
svg {
font-size: 14px;
}
.foregroundlines {
fill: none;
stroke-opacity: 0.3;
stroke-width: 1.5px;
}
.foregroundlines.fade {
display:none
}
.legend line {
stroke-width: 2px;
}
.brush .extent {
fill-opacity: .3;
stroke: #fff;
shape-rendering: crispEdges;
}
.axis line, .axis path {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.axis text {
text-shadow: 0 1px 0 #fff;
cursor: move;
}
</style>
<kibana-panel ng-controller='parallelcoordinates' ng-init="init()">
<span ng-show="panel.spyable" style="position:absolute;right:0px;top:0px" class='panelextra pointer'>
<i bs-modal="'partials/modal.html'" class="icon-eye-open"></i>
</span>
<div parallelcoordinates params="{{panel}}" style="height:{{panel.height || row.height}}"></div>
</kibana-panel>

View File

@ -1,514 +0,0 @@
angular.module('kibana.parallelcoordinates', [])
.controller('parallelcoordinates', function ($scope, eventBus) {
$scope.activeDocs = [];
// Set and populate defaults
var _d = {
status : "Broken",
query : "*",
size : 100, // Per page
pages : 5, // Pages available
offset : 0,
sort : ['@timestamp','desc'],
group : "default",
style : {'font-size': '9pt'},
fields : [],
sortable: true,
spyable: true
}
_.defaults($scope.panel, _d)
$scope.init = function () {
$scope.set_listeners($scope.panel.group);
// Now that we're all setup, request the time from our group
eventBus.broadcast($scope.$id,$scope.panel.group,"get_time")
//and get the currently selected fields
eventBus.broadcast($scope.$id,$scope.panel.group,"get_fields")
};
$scope.set_listeners = function(group) {
eventBus.register($scope,'time',function(event,time) {
$scope.panel.offset = 0;
set_time(time)
});
eventBus.register($scope,'query',function(event,query) {
$scope.panel.offset = 0;
$scope.panel.query = _.isArray(query) ? query[0] : query;
$scope.get_data();
});
eventBus.register($scope,'sort', function(event,sort){
$scope.panel.sort = _.clone(sort);
$scope.get_data();
});
eventBus.register($scope,'selected_fields', function(event, fields) {
$scope.panel.fields = _.clone(fields)
$scope.$emit('render');
});
};
$scope.get_data = function (segment,query_id) {
// Make sure we have everything for the request to complete
if (_.isUndefined($scope.index) || _.isUndefined($scope.time))
return;
var _segment = _.isUndefined(segment) ? 0 : segment
$scope.segment = _segment;
$scope.panel.loading = true;
var request = $scope.ejs.Request().indices($scope.index[_segment])
.query(ejs.FilteredQuery(
ejs.QueryStringQuery($scope.panel.query || '*'),
ejs.RangeFilter($scope.time.field)
.from($scope.time.from)
.to($scope.time.to)
)
)
.size($scope.panel.size*$scope.panel.pages)
.sort($scope.panel.sort[0],$scope.panel.sort[1]);
$scope.populate_modal(request);
var results = request.doSearch();
// Populate scope when we have results
results.then(function (results) {
$scope.panel.loading = false;
if(_segment === 0) {
$scope.hits = 0;
$scope.data = [];
query_id = $scope.query_id = new Date().getTime()
}
// Check for error and abort if found
if(!(_.isUndefined(results.error))) {
$scope.panel.error = $scope.parse_error(results.error);
return;
}
// Check that we're still on the same query, if not stop
if($scope.query_id === query_id) {
$scope.data= $scope.data.concat(_.map(results.hits.hits, function(hit) {
return flatten_json(hit['_source']);
}));
$scope.hits += results.hits.total;
// Sort the data
$scope.data = _.sortBy($scope.data, function(v){
return v[$scope.panel.sort[0]]
});
// Reverse if needed
if($scope.panel.sort[1] == 'desc')
$scope.data.reverse();
// Keep only what we need for the set
$scope.data = $scope.data.slice(0,$scope.panel.size * $scope.panel.pages)
} else {
return;
}
$scope.$emit('render')
});
};
// I really don't like this function, too much dom manip. Break out into directive?
$scope.populate_modal = function (request) {
$scope.modal = {
title: "Inspector",
body: "<h5>Last Elasticsearch Query</h5><pre>" + 'curl -XGET ' + config.elasticsearch + '/' + $scope.index + "/_search?pretty -d'\n" + angular.toJson(JSON.parse(request.toString()), true) + "'</pre>"
}
};
function set_time(time) {
$scope.time = time;
$scope.index = _.isUndefined(time.index) ? $scope.index : time.index
$scope.get_data();
}
$scope.$watch('activeDocs', function(v) {
eventBus.broadcast($scope.$id,$scope.panel.group,"table_documents",
{query:$scope.panel.query,docs:$scope.activeDocs});
});
})
.directive('parallelcoordinates', function () {
return {
restrict: 'A',
link: function (scope, elem, attrs) {
//used to store a variety of directive-level variables
var directive = {};
scope.initializing = false;
/**
* Initialize the panels if new, or render existing panels
*/
scope.init_or_render = function() {
if (typeof directive.svg === 'undefined') {
//prevent duplicate initialization steps, if render is called again
//before the svg is setup
if (!scope.initializing) {
init_panel();
}
} else {
render_panel();
}
};
/**
* Receive render events
*/
scope.$on('render', function () {
scope.init_or_render();
});
/**
* On window resize, re-render the panel
*/
angular.element(window).bind('resize', function () {
scope.init_or_render();
});
/**
* Load the various panel-specific scripts then initialize
* the svg and set appropriate D3 settings
*/
function init_panel() {
directive.m = [80, 100, 80, 100];
directive.w = $(elem[0]).width() - directive.m[1] - directive.m[3];
directive.h = $(elem[0]).height() - directive.m[0] - directive.m[2];
scope.initializing = true;
// Using LABjs, wait until all scripts are loaded before rendering panel
var scripts = $LAB.script("common/lib/d3.v3.min.js?rand="+Math.floor(Math.random()*10000));
scripts.wait(function () {
directive.x = d3.scale.ordinal().domain(scope.panel.fields).rangePoints([0, directive.w]);
directive.y = {};
directive.line = d3.svg.line().interpolate('cardinal');
directive.axis = d3.svg.axis().orient("left");
var viewbox = "0 0 " + (directive.w + directive.m[1] + directive.m[3]) + " " + (directive.h + directive.m[0] + directive.m[2]);
directive.svg = d3.select(elem[0]).append("svg")
.attr("width", "100%")
.attr("height", "100%")
.attr("viewbox", viewbox)
.append("svg:g")
.attr("transform", "translate(" + directive.m[3] + "," + directive.m[0] + ")");
// Add foreground lines.
directive.foreground = directive.svg.append("svg:g")
.attr("class", "foreground");
scope.initializing = false;
render_panel();
});
}
// Returns the path for a given data point.
function path(d) {
return directive.line(scope.panel.fields.map(function(p) { return [directive.x(p), directive.y[p](d[p])]; }));
}
// Handles a brush event, toggling the display of foreground lines.
function brush() {
var actives = scope.panel.fields.filter(function(p) { return !directive.y[p].brush.empty(); }),
extents = actives.map(function(p) { return directive.y[p].brush.extent(); });
//.fade class hides the "inactive" lines, helps speed up rendering significantly
directive.foregroundLines.classed("fade", function(d) {
return !actives.every(function(p, i) {
var pointValue;
if (directive.ordinals[p] === true) {
pointValue = directive.y[p](d[p]);
} else {
pointValue = d[p];
}
var inside = extents[i][0] <= pointValue && pointValue <= extents[i][1];
return inside;
});
});
//activeDocs contains the actual doc records for selected lines.
//will be broadcast out to the table
var activeDocs = _.filter(scope.data, function(v) {
return actives.every(function(p,i) {
var inside = extents[i][0] <= v[p] && v[p] <= extents[i][1];
return inside;
});
})
scope.$apply(function() {
scope.activeDocs = activeDocs;
});
}
//Drag functions are used for dragging the axis aroud
function dragstart(d) {
directive.i = scope.panel.fields.indexOf(d);
}
function drag(d) {
directive.x.range()[directive.i] = d3.event.x;
scope.panel.fields.sort(function(a, b) { return directive.x(a) - directive.x(b); });
directive.foregroundLines.attr("transform", function(d) { return "translate(" + directive.x(d) + ")"; });
directive.traits.attr("transform", function(d) { return "translate(" + directive.x(d) + ")"; });
directive.brushes.attr("transform", function(d) { return "translate(" + directive.x(d) + ")"; });
directive.axisLines.attr("transform", function(d) { return "translate(" + directive.x(d) + ")"; });
directive.foregroundLines.attr("d", path);
}
function dragend(d) {
directive.x.domain(scope.panel.fields).rangePoints([0, directive.w]);
var t = d3.transition().duration(500);
t.selectAll(".trait").attr("transform", function(d) { return "translate(" + directive.x(d) + ")"; });
t.selectAll(".axis").attr("transform", function(d) { return "translate(" + directive.x(d) + ")"; });
t.selectAll(".brush").attr("transform", function(d) { return "translate(" + directive.x(d) + ")"; });
t.selectAll(".foregroundlines").attr("d", path);
}
/**
* Render updates to the SVG. Typically happens when the data changes (time, query)
* or when new options are selected
*/
function render_panel() {
//update the svg if the size has changed
directive.w = $(elem[0]).width() - directive.m[1] - directive.m[3];
directive.h = $(elem[0]).height() - directive.m[0] - directive.m[2];
directive.svg.attr("viewbox", "0 0 " + (directive.w + directive.m[1] + directive.m[3]) + " " + (directive.h + directive.m[0] + directive.m[2]));
directive.x = d3.scale.ordinal().domain(scope.panel.fields).rangePoints([0, directive.w]);
directive.y = {};
directive.line = d3.svg.line().interpolate('cardinal');
directive.axis = d3.svg.axis().orient("left").ticks(5);
directive.ordinals = {};
scope.panel.fields.forEach(function(d) {
var firstField = scope.data[0][d];
if (_.isString(firstField)) {
if (isValidDate(new Date(firstField))) {
//convert date timestamps to actual dates
_.map(scope.data, function(v) {
v[d] = new Date(v[d]);
});
var extents = d3.extent(scope.data, function(p) { return p[d]; });
directive.y[d] = d3.time.scale()
.domain([extents[0],extents[1]])
.range([directive.h, 0]);
} else {
directive.ordinals[d] = true;
var value = function(v) { return v[d]; };
var values = _.map(_.uniq(scope.data, value),value);
directive.y[d] = d3.scale.ordinal()
.domain(values)
.rangePoints([directive.h, 0]);
}
} else if (_.isNumber(firstField)) {
directive.y[d] = d3.scale.linear()
.domain(d3.extent(scope.data, function(p) { return +p[d]; }))
.range([directive.h, 0]);
} else if (_.isDate(firstField)) {
//this section is only used after timestamps have been parsed into actual date objects...
//avoids reparsing
var extents = d3.extent(scope.data, function(p) { return p[d]; });
directive.y[d] = d3.time.scale()
.domain([extents[0],extents[1]])
.range([directive.h, 0]);
}
directive.y[d].brush = d3.svg.brush()
.y(directive.y[d])
.on("brush", brush);
});
//setup the colors for the lines
setColors();
//pull out the actively selected columns for rendering the axis/lines
var activeData = _.map(scope.data, function(d) {
var t = {};
_.each(scope.panel.fields, function(f) {
t[f] = d[f];
});
return t;
});
//Lines
directive.foregroundLines = directive.foreground
.selectAll(".foregroundlines")
.data(activeData, function(d, i){
var id = "";
_.each(d, function(v) {
id += i + "_" + v;
});
return id;
});
directive.foregroundLines
.enter().append("svg:path")
.attr("d", path)
.attr("class", "foregroundlines")
.attr("style", function(d) {
return "stroke:" + directive.colors(d[scope.panel.fields[0]]) + ";";
});
directive.foregroundLines.exit().remove();
//Axis group
directive.traits = directive.svg.selectAll(".trait")
.data(scope.panel.fields, String);
directive.traits
.enter().append("svg:g")
.attr("class", "trait")
.attr("transform", function(d) { return "translate(" + directive.x(d) + ")"; });
directive.traits
.exit().remove();
//brushes used to select lines
directive.brushes = directive.svg.selectAll(".brush")
.data(scope.panel.fields, String);
directive.brushes
.enter()
.append("svg:g")
.attr("class", "brush")
.each(function(d) {
d3.select(this)
.call(directive.y[d].brush)
.attr("transform", function(d) { return "translate(" + directive.x(d) + ")"; });
})
.selectAll("rect")
.attr("x", -8)
.attr("width", 16);
//this section is repeated because enter() only works on "new" data, but we always need to
//update the brushes if things change. This just calls the brushing function, so it doesn't
//affect currently active rects
directive.brushes
.each(function(d) {
d3.select(this)
.call(directive.y[d].brush)
.attr("transform", function(d) { return "translate(" + directive.x(d) + ")"; });
});
directive.brushes
.exit().remove();
//vertical axis and labels
directive.axisLines = directive.svg.selectAll(".axis")
.data(scope.panel.fields, String);
directive.axisLines
.enter()
.append("svg:g")
.attr("class", "axis")
.each(function(d) {
d3.select(this)
.call(directive.axis.scale(directive.y[d]))
.attr("transform", function(d) { return "translate(" + directive.x(d) + ")"; });
}).call(d3.behavior.drag()
.origin(function(d) { return {x: directive.x(d)}; })
.on("dragstart", dragstart)
.on("drag", drag)
.on("dragend", dragend))
.append("svg:text")
.attr("text-anchor", "middle")
.attr("y", -9)
.text(String);
directive.axisLines
.exit().remove();
//Simulate a dragend in case there is new data and we need to rearrange
dragend();
}
function setColors() {
var firstPanelField = scope.data[0][scope.panel.fields[0]];
var extents = d3.extent(scope.data, function(p) { return p[scope.panel.fields[0]]; });
if (_.isString(firstPanelField)) {
var value = function(v) { return v[firstPanelField]; };
var values = _.map(_.uniq(scope.data, value),value);
values = scope.data;
directive.colors = d3.scale.ordinal()
.domain(values)
.range(d3.range(values.length).map(d3.scale.linear()
.domain([0, values.length - 1])
.range(["red", "blue"])
.interpolate(d3.interpolateLab)));
} else if (_.isNumber(firstPanelField)) {
directive.colors = d3.scale.linear()
.domain([extents[0],extents[1]])
.range(["#4580FF", "#FF9245"]);
} else if (_.isDate(firstPanelField)) {
directive.colors = d3.time.scale()
.domain([extents[0],extents[1]])
.range(["#4580FF", "#FF9245"]);
}
}
function isValidDate(d) {
if ( Object.prototype.toString.call(d) !== "[object Date]" )
return false;
return !isNaN(d.getTime());
}
}
};
});

View File

@ -1,61 +1,39 @@
<div class="row-fluid" ng-switch="panel.mode">
<div class="span3">
<div>
<div class="row-fluid" ng-switch="panel.mode">
<div class="row-fluid">
<div class="span2">
<label class="small">Mode</label>
<select class="input-small" ng-change="set_mode(panel.mode)" ng-model="panel.mode" ng-options="f for f in ['terms','goal']"></select>
</div>
<select class="input-small" ng-change="set_mode(panel.mode);set_refresh(true)" ng-model="panel.mode" ng-options="f for f in ['terms','goal']"></select>
</div>
</div>
<div ng-switch-when="terms">
<div class="row-fluid">
<div class="span3">
<form style="margin-bottom: 0px">
<div class="span2">
<label class="small">Field</label>
<input type="text" style="width:90%" bs-typeahead="fields.list" ng-model="panel.query.field">
</form>
<input type="text" class="input-small" bs-typeahead="fields.list" ng-model="panel.query.field" ng-change="set_refresh(true)">
</div>
<div class="span5">
<form class="input-append" style="margin-bottom: 0px">
<label class="small">Query</label>
<input type="text" style="width:80%" ng-model="panel.query.query">
<button class="btn" ng-click="get_data()"><i class="icon-search"></i></button>
</form>
</div>
</div>
<div class="row-fluid">
<div class="span3">
<div class="span2">
<label class="small">Length</label>
<input type="number" style="width:80%" ng-model="panel.size" ng-change="get_data()">
<input class="input-small" type="number" ng-model="panel.size" ng-change="set_refresh(true)">
</div>
<div class="span8">
<form class="input-append" style="margin-bottom: 0px">
<div class="span6">
<label class="small">Exclude Terms(s) (comma seperated)</label>
<input array-join type="text" style="width:90%" ng-model='panel.exclude'></input>
<button class="btn" ng-click="get_data()"><i class="icon-search"></i></button>
</form>
<input array-join type="text" ng-model='panel.exclude'></input>
</div>
</div>
</div>
<div ng-switch-when="goal">
<div class="row-fluid">
<div class="span3">
<label class="small">Mode</label>
<select class="input-small" ng-change="set_mode(panel.mode)" ng-model="panel.mode" ng-options="f for f in ['terms','goal']"></select>
</div>
<div class="span2">
<form style="margin-bottom: 0px">
<label class="small">Goal</label>
<input type="number" style="width:90%" ng-model="panel.query.goal">
</form>
</div>
<div class="span5">
<form class="input-append" style="margin-bottom: 0px">
<label class="small">Query</label>
<input type="text" style="width:80%" ng-model="panel.query.query">
<button class="btn" ng-click="get_data()"><i class="icon-search"></i></button>
<input type="number" style="width:90%" ng-model="panel.query.goal" ng-change="set_refresh(true)">
</form>
</div>
</div>
</div>
</div>
<div class="row-fluid">
</div>
<div class="row-fluid">
<div class="span1">
<label class="small"> Donut </label><input type="checkbox" ng-model="panel.donut" ng-checked="panel.donut">
</div>
@ -69,9 +47,9 @@
<label class="small">Legend</label>
<select class="input-small" ng-model="panel.legend" ng-options="f for f in ['above','below','none']"></select></span>
</div>
</div>
<h5>Panel Spy</h5>
<div class="row-fluid">
</div>
<h5>Panel Spy</h5>
<div class="row-fluid">
<div class="span2">
<label class="small"> Spyable </label><input type="checkbox" ng-model="panel.spyable" ng-checked="panel.spyable">
</div>
@ -80,4 +58,5 @@
be accessed by clicking the <i class='icon-eye-open'></i> in the top right
of the panel.
</div>
</div>
</div>

View File

@ -23,27 +23,20 @@
doesn't have a field
* spyable :: Show the 'eye' icon that displays the last ES query for this panel
### Group Events
#### Sends
* get_time :: On panel initialization get time range to query
#### Receives
* time :: An object containing the time range to use and the index(es) to query
* query :: An Array of queries, this panel will use the first in the array
*/
angular.module('kibana.pie', [])
.controller('pie', function($scope, eventBus) {
.controller('pie', function($scope, $rootScope, query, dashboard, filterSrv) {
// Set and populate defaults
var _d = {
status : "Deprecating Soon",
query : { field:"_all", query:"*", goal: 1},
query : { field:"_type", goal: 100},
size : 10,
exclude : [],
donut : false,
tilt : false,
legend : true,
legend : "above",
labels : true,
mode : "terms",
group : "default",
@ -53,30 +46,7 @@ angular.module('kibana.pie', [])
_.defaults($scope.panel,_d)
$scope.init = function() {
eventBus.register($scope,'time', function(event,time){set_time(time)});
eventBus.register($scope,'query', function(event, query) {
$scope.panel.query.query = _.isArray(query) ? query[0] : query;
$scope.get_data();
});
// Now that we're all setup, request the time from our group
eventBus.broadcast($scope.$id,$scope.panel.group,'get_time')
}
$scope.remove_query = function(q) {
if($scope.panel.mode !== 'query')
return false;
$scope.panel.query = _.without($scope.panel.query,q);
$scope.get_data();
}
$scope.add_query = function(label,query) {
if($scope.panel.mode !== 'query')
return false;
$scope.panel.query.unshift({
query: query,
label: label,
});
$scope.$on('refresh',function(){$scope.get_data()})
$scope.get_data();
}
@ -84,21 +54,40 @@ angular.module('kibana.pie', [])
switch(mode)
{
case 'terms':
$scope.panel.query = {query:"*",field:"_all"};
$scope.panel.query = {field:"_all"};
break;
case 'goal':
$scope.panel.query = {query:"*",goal:100};
$scope.panel.query = {goal:100};
break;
}
}
$scope.set_refresh = function (state) {
$scope.refresh = state;
}
$scope.close_edit = function() {
if($scope.refresh)
$scope.get_data();
$scope.refresh = false;
$scope.$emit('render');
}
$scope.get_data = function() {
// Make sure we have everything for the request to complete
if(_.isUndefined($scope.index) || _.isUndefined($scope.time))
if(dashboard.indices.length == 0) {
return
}
$scope.panel.loading = true;
var request = $scope.ejs.Request().indices($scope.index);
var request = $scope.ejs.Request().indices(dashboard.indices);
// This could probably be changed to a BoolFilter
var boolQuery = ejs.BoolQuery();
_.each(query.list,function(q) {
boolQuery = boolQuery.should(ejs.QueryStringQuery(q.query || '*'))
})
// Terms mode
if ($scope.panel.mode == "terms") {
@ -109,10 +98,8 @@ angular.module('kibana.pie', [])
.exclude($scope.panel.exclude)
.facetFilter(ejs.QueryFilter(
ejs.FilteredQuery(
ejs.QueryStringQuery($scope.panel.query.query || '*'),
ejs.RangeFilter($scope.time.field)
.from($scope.time.from)
.to($scope.time.to)
boolQuery,
filterSrv.getBoolFilter(filterSrv.ids)
)))).size(0)
$scope.populate_modal(request);
@ -141,11 +128,8 @@ angular.module('kibana.pie', [])
// Goal mode
} else {
request = request
.query(ejs.QueryStringQuery($scope.panel.query.query || '*'))
.filter(ejs.RangeFilter($scope.time.field)
.from($scope.time.from)
.to($scope.time.to)
.cache(false))
.query(boolQuery)
.filter(filterSrv.getBoolFilter(filterSrv.ids))
.size(0)
$scope.populate_modal(request);
@ -169,26 +153,14 @@ angular.module('kibana.pie', [])
$scope.modal = {
title: "Inspector",
body : "<h5>Last Elasticsearch Query</h5><pre>"+
'curl -XGET '+config.elasticsearch+'/'+$scope.index+"/_search?pretty -d'\n"+
'curl -XGET '+config.elasticsearch+'/'+dashboard.indices+"/_search?pretty -d'\n"+
angular.toJson(JSON.parse(request.toString()),true)+
"'</pre>",
}
}
$scope.build_search = function(field,value) {
$scope.panel.query.query = add_to_query($scope.panel.query.query,field,value,false)
$scope.get_data();
eventBus.broadcast($scope.$id,$scope.panel.group,'query',[$scope.panel.query.query]);
}
function set_time(time) {
$scope.time = time;
$scope.index = _.isUndefined(time.index) ? $scope.index : time.index
$scope.get_data();
}
})
.directive('pie', function() {
.directive('pie', function(query, filterSrv, dashboard) {
return {
restrict: 'A',
link: function(scope, elem, attrs) {
@ -228,8 +200,8 @@ angular.module('kibana.pie', [])
show: scope.panel.labels,
radius: 2/3,
formatter: function(label, series){
return '<div ng-click="build_search(panel.query.field,\''+label+'\') "style="font-size:8pt;text-align:center;padding:2px;color:white;">'+
label+'<br/>'+Math.round(series.percent)+'%</div>';
return '<div "style="font-size:8pt;text-align:center;padding:2px;color:white;">'+
series.info.alias+'<br/>'+Math.round(series.percent)+'%</div>';
},
threshold: 0.1
}
@ -258,7 +230,7 @@ angular.module('kibana.pie', [])
clickable: true
},
legend: { show: false },
colors: ['#86B22D','#BF6730','#1D7373','#BFB930','#BF3030','#77207D']
colors: query.colors
};
// Populate element
@ -269,7 +241,7 @@ angular.module('kibana.pie', [])
}
}
function piett(x, y, contents) {
function tt(x, y, contents) {
var tooltip = $('#pie-tooltip').length ?
$('#pie-tooltip') : $('<div id="pie-tooltip"></div>');
@ -287,16 +259,19 @@ angular.module('kibana.pie', [])
}
elem.bind("plotclick", function (event, pos, object) {
if (!object)
if (!object) {
return;
if(scope.panel.mode === 'terms')
scope.build_search(scope.panel.query.field,object.series.label);
}
if(scope.panel.mode === 'terms') {
filterSrv.set({type:'terms',field:scope.panel.query.field,value:object.series.label})
dashboard.refresh();
}
});
elem.bind("plothover", function (event, pos, item) {
if (item) {
var percent = parseFloat(item.series.percent).toFixed(1) + "%";
piett(pos.pageX, pos.pageY, "<div style='vertical-align:middle;display:inline-block;background:"+item.series.color+";height:15px;width:15px;border-radius:10px;'></div> " +
tt(pos.pageX, pos.pageY, "<div style='vertical-align:middle;display:inline-block;background:"+item.series.color+";height:15px;width:15px;border-radius:10px;'></div> " +
(item.series.label||"")+ " " + percent);
} else {
$("#pie-tooltip").remove();

7
panels/query/editor.html Normal file
View File

@ -0,0 +1,7 @@
<div>
<div class="row-fluid">
<div class="span12">
No options here
</div>
</div>
</div>

15
panels/query/meta.html Normal file
View File

@ -0,0 +1,15 @@
<div>
<style>
.input-query-alias {
margin-bottom: 5px !important;
}
</style>
<a class="close" ng-click="render();dismiss();" href="">×</a>
<h6>Query Alias</h6>
<form>
<input class="input-medium input-query-alias" type="text" ng-model="queries.list[id].alias" placeholder='Alias...' />
<div>
<i ng-repeat="color in queries.colors" class="pointer" ng-class="{'icon-circle-blank':queries.list[id].color == color,'icon-circle':queries.list[id].color != color}" style="color:{{color}}" ng-click="queries.list[id].color = color;render();"> </i>
</div>
</form>
</div>

51
panels/query/module.html Normal file
View File

@ -0,0 +1,51 @@
<kibana-panel ng-controller='query' ng-init="init()">
<style>
.short-query {
display:inline-block;
margin-left: 10px;
}
.begin-query {
position:absolute;
left:15px;
top:5px;
}
.end-query {
position:absolute;
right:15px;
top:5px;
}
.panel-query {
padding-left: 35px !important;
height: 31px !important;
-webkit-box-sizing: border-box; /* Safari/Chrome, other WebKit */
-moz-box-sizing: border-box; /* Firefox, other Gecko */
box-sizing: border-box; /* Opera/IE 8+ */
}
.form-search:hover .has-remove {
padding-left: 50px !important;
}
.remove-query {
opacity: 0;
}
.last-query {
padding-right: 45px !important;
}
.form-search:hover .remove-query {
opacity: 1;
}
</style>
<label class="small">{{panel.label}}</label>
<div ng-repeat="id in queries.ids" ng-class="{'short-query': queries.ids.length>1}">
<form class="form-search" style="position:relative" ng-submit="refresh()">
<span class="begin-query">
<i class="icon-circle pointer" data-unique="1" bs-popover="'panels/query/meta.html'" data-placement="right" style="color:{{queries.list[id].color}}"></i>
<i class="icon-remove-sign pointer remove-query" ng-show="queries.ids.length>1" ng-click="queries.remove(id);refresh()"></i>
</span>
<input class="search-query panel-query" ng-class="{'input-block-level': queries.ids.length==1,'last-query': $last,'has-remove': queries.ids.length>1}" bs-typeahead="panel.history" data-min-length=0 data-items=100 type="text" ng-model="queries.list[id].query"/>
<span class="end-query">
<i class="icon-search pointer" ng-click="refresh()" ng-show="$last"></i>
<i class="icon-plus pointer" ng-click="queries.set({})" ng-show="$last"></i>
</span>
</form>
</div>
</kibana-panel>

60
panels/query/module.js Normal file
View File

@ -0,0 +1,60 @@
/*
## query
An experimental panel for the query service
### Parameters
* label :: The label to stick over the field
* query :: A string or an array of querys. String if multi is off, array if it is on
This should be fixed, it should always be an array even if its only
one element
*/
angular.module('kibana.query', [])
.controller('query', function($scope, query, $rootScope) {
// Set and populate defaults
var _d = {
status : "Experimental",
label : "Search",
query : "*",
group : "default",
history : [],
remember: 10 // max: 100, angular strap can't take a variable for items param
}
_.defaults($scope.panel,_d);
$scope.queries = query;
$scope.init = function() {
}
$scope.refresh = function(query) {
$rootScope.$broadcast('refresh')
}
$scope.render = function(query) {
$rootScope.$broadcast('render')
}
$scope.add_query = function() {
if (_.isArray($scope.panel.query))
$scope.panel.query.push("")
else {
$scope.panel.query = new Array($scope.panel.query)
$scope.panel.query.push("")
}
}
var update_history = function(query) {
if($scope.panel.remember > 0) {
$scope.panel.history = _.union(query.reverse(),$scope.panel.history)
var _length = $scope.panel.history.length
if(_length > $scope.panel.remember) {
$scope.panel.history = $scope.panel.history.slice(0,$scope.panel.remember)
}
}
}
});

View File

@ -1,5 +0,0 @@
<kibana-panel ng-controller='sort' ng-init="init()" style="white-space: nowrap;">
<label><small>{{panel.label}}</small></label>
<select style="width:85%" ng-model="panel.sort[0]" ng-change="set_sort()" ng-options="f for f in fields"></select>
<i ng-click="toggle_sort()" class="pointer" ng-class="{'icon-chevron-up': panel.sort[1] == 'asc','icon-chevron-down': panel.sort[1] == 'desc'}"></i>
</kibana-panel>

View File

@ -1,51 +0,0 @@
/*
## Sort
This will probably be removed in the near future since it only interacts with
the table panel and the table panel already implements all of its functionality.
It only interacts with the table panel in any case
### Parameters
* label :: The label to stick over the drop down
* sort :: An array where the first elemetn is the field to sort on an the second
is the direction ('asc' or 'desc')
### Group Events
#### Sends
* sort :: An array where the first elemetn is the field to sort on an the second
is the direction ('asc' or 'desc')
#### Receives
* fields :: An array containing the fields in a table. This will be concat'd +
uniqued with the curent list.
*/
angular.module('kibana.sort', [])
.controller('sort', function($scope, eventBus) {
// Set and populate defaults
var _d = {
status : "Stable",
label : "Sort",
sort : ['_score','desc'],
group : "default"
}
_.defaults($scope.panel,_d);
$scope.init = function() {
$scope.fields = [];
eventBus.register($scope,'fields',function(event, fields) {
$scope.panel.sort = _.clone(fields.sort);
$scope.fields = _.union(fields.all,$scope.fields);
});
}
$scope.set_sort = function() {
eventBus.broadcast($scope.$id,$scope.panel.group,"sort",$scope.panel.sort)
}
$scope.toggle_sort = function() {
$scope.panel.sort[1] = $scope.panel.sort[1] == 'asc' ? 'desc' : 'asc';
$scope.set_sort();
}
})

View File

@ -1,17 +0,0 @@
<div>
<div class="row-fluid">
<div class="span2">
<label class="small">Multi-query</label><input type="checkbox" ng-change="set_multi(panel.multi) "ng-model="panel.multi" ng-checked="panel.multi">
</div>
<div class="span3" ng-show="panel.multi">
<label class="small">Arrangement</label>
<select class="input-small" ng-model="panel.multi_arrange" ng-options="f for f in ['vertical','horizontal']"></select>
</div>
</div>
<div class="row-fluid" ng-show="panel.multi">
<div class="span12">
<h5>A note on multi query panels</h5>
<p>You turned on multi panel support: <i>cool</i>. Be aware that not all panels support display of multiple queries. Panels that don't support receiving several queries at one time will (should) display the results of the <strong>first query in the list</strong>. Further, some panels might not support receiving multiple queries in all modes. For example, the <strong>pie panel</strong> supports multiple queries only in <strong>query mode</strong>. In its other modes it will display the results of the first query in the list</p>
</div>
</div>
</div>

View File

@ -1,49 +0,0 @@
<kibana-panel ng-controller='stringquery' ng-init="init()">
<div ng-show="!panel.multi">
<form>
<table class="form-horizontal">
<tr><td><label><small>{{panel.label}}</small></label></td></tr>
<tr>
<td width="97%" style="padding-right:20px">
<span style="position:relative">
<i class="icon-remove-sign pointer" style="position:absolute;left:10px;top:3px" ng-show="panel.query.length > 0" ng-click="panel.query='';send_query(panel.query)"></i>
<input bs-typeahead="panel.history" data-min-length=0 data-items=100 type="text" style="text-indent:20px;width:100%" ng-model="panel.query">
</span>
</td>
<td style="margin-left:20px" width="1%">
<button style="margin-top:0px" type="submit" class="btn btn-success btn-small" ng-click="send_query(panel.query)"><i class="icon-search"></i></button>
</td>
<tr>
</table>
</form>
</div>
<div class='row-fluid' ng-show="panel.multi && panel.multi_arrange == 'horizontal'">
<span ng-repeat="q in panel.query">
<span style="margin-bottom:0px;margin-right:5px;display:inline-block;position:relative">
<i class="icon-remove-sign pointer" style="position:absolute;left:10px;top:8px" ng-show="panel.query.length > 1" ng-click="remove_query($index);send_query(panel.query)"></i>
<input bs-typeahead="panel.history" data-min-length=0 data-items=100 style="margin-bottom:5px; text-indent: 20px;" type="text" ng-model="panel.query[$index]" ng-model-onblur style="width:90%">
<br>
</span>
</span>
<br>
<button type="submit" class="btn btn-info" ng-click="send_query(panel.query)"><i class="icon-search"></i> Search</button>
<button type="submit" class="btn" ng-click="add_query();"><i class="icon-plus"></i></button>
</div>
<div ng-show="panel.multi && panel.multi_arrange == 'vertical'">
<form>
<table class="form-horizontal" style="margin-bottom:5px;">
<tr><td class="small">Queries</td></tr>
<tr ng-repeat="q in panel.query" style="margin-bottom:10px">
<td width="99%">
<span style="position:relative">
<i class="icon-remove-sign pointer" style="position:absolute;left:10px;top:3px" ng-show="panel.query.length > 1" ng-click="remove_query($index);send_query(panel.query)"></i>
<input bs-typeahead="panel.history" data-min-length=0 data-items=100 type="text" ng-model="panel.query[$index]" ng-model-onblur style="text-indent: 20px;width:100%">
</span>
</td>
<td width="1%" style="visibility:hidden"><button class="btn btn-info" type="submit"></button></td>
</tr>
</table>
<button type="submit" class="btn btn-info" ng-click="send_query(panel.query)"><i class="icon-search"></i> Search</button>
<button type="submit" class="btn" ng-click="send_query(panel.query);add_query();"><i class="icon-plus"></i> Add Query</button>
</form>
</kibana-panel>

View File

@ -1,81 +0,0 @@
/*
## Stringquery
Broadcasts a query object to other panels
### Parameters
* label :: The label to stick over the field
* query :: A string or an array of querys. String if multi is off, array if it is on
This should be fixed, it should always be an array even if its only
one element
* multi :: Allow input of multiple queries? true/false
* multi_arrange :: How to arrange multu query string panels, 'vertical' or 'horizontal'
### Group Events
#### Sends
* query :: Always broadcast as an array, even in multi: false
#### Receives
* query :: An array of queries. This is probably needs to be fixed.
*/
angular.module('kibana.stringquery', [])
.controller('stringquery', function($scope, eventBus) {
// Set and populate defaults
var _d = {
status : "Stable",
label : "Search",
query : "*",
group : "default",
multi : false,
multi_arrange: 'horizontal',
history : [],
remember: 10 // max: 100, angular strap can't take a variable for items param
}
_.defaults($scope.panel,_d);
$scope.init = function() {
// If we're in multi query mode, they all get wiped out if we receive a
// query. Query events must be exchanged as arrays.
eventBus.register($scope,'query',function(event,query) {
$scope.panel.query = query;
update_history(query);
});
}
$scope.send_query = function(query) {
var _query = _.isArray(query) ? query : [query];
update_history(_query);
eventBus.broadcast($scope.$id,$scope.panel.group,'query',_query);
}
$scope.add_query = function() {
if (_.isArray($scope.panel.query))
$scope.panel.query.push("")
else {
$scope.panel.query = new Array($scope.panel.query)
$scope.panel.query.push("")
}
}
$scope.set_multi = function(multi) {
$scope.panel.query = multi ?
new Array($scope.panel.query) : $scope.panel.query[0];
}
$scope.remove_query = function(index) {
$scope.panel.query.splice(index,1);
}
var update_history = function(query) {
if($scope.panel.remember > 0) {
$scope.panel.history = _.union(query.reverse(),$scope.panel.history)
var _length = $scope.panel.history.length
if(_length > $scope.panel.remember) {
$scope.panel.history = $scope.panel.history.slice(0,$scope.panel.remember)
}
}
}
});

View File

@ -1,12 +1,3 @@
<div class="row-fluid">
<div style="width:90%">
<form class="input-append">
<h6>Query</h6>
<input type="text" style="width:90%" ng-model="panel.query">
<button class="btn" ng-click="get_data();"><i class="icon-search"></i></button>
</form>
</div>
</div>
<div class="row-fluid">
<div class="span4">
<form class="input-append">
@ -77,14 +68,6 @@
<select class="input-small" ng-model="panel.overflow" ng-options="f.value as f.key for f in [{key:'scroll',value:'height'},{key:'expand',value:'min-height'}]"></select>
</div>
</div>
<!--<div class="row-fluid" ng-show='panel.sortable'>
<div class="span11">
<h6>A note about sorting</h6>
Allowing sorting can incur a significant performance penalty if using timestamped indices.
Kibana will be unable to query your indices sequentially and thus must query them all at
once. Only enable sorting if your cluster is stout enough to handle it.
</div>
</div>-->
<h5>Panel Spy</h5>
<div class="row-fluid">
<div class="span2">

View File

@ -29,7 +29,7 @@
*/
angular.module('kibana.table', [])
.controller('table', function($scope, eventBus, fields) {
.controller('table', function($rootScope, $scope, eventBus, fields, query, dashboard, filterSrv) {
// Set and populate defaults
var _d = {
@ -55,20 +55,11 @@ angular.module('kibana.table', [])
$scope.set_listeners($scope.panel.group)
// Now that we're all setup, request the time from our group
eventBus.broadcast($scope.$id,$scope.panel.group,"get_time")
$scope.get_data();
}
$scope.set_listeners = function(group) {
eventBus.register($scope,'time',function(event,time) {
$scope.panel.offset = 0;
set_time(time)
});
eventBus.register($scope,'query',function(event,query) {
$scope.panel.offset = 0;
$scope.panel.query = _.isArray(query) ? query[0] : query;
$scope.get_data();
});
$scope.$on('refresh',function(){$scope.get_data()})
eventBus.register($scope,'sort', function(event,sort){
$scope.panel.sort = _.clone(sort);
$scope.get_data();
@ -77,7 +68,7 @@ angular.module('kibana.table', [])
$scope.panel.fields = _.clone(fields)
});
eventBus.register($scope,'table_documents', function(event, docs) {
$scope.panel.query = docs.query;
query.list[query.ids[0]].query = docs.query;
$scope.data = docs.docs;
});
}
@ -116,32 +107,37 @@ angular.module('kibana.table', [])
}
$scope.build_search = function(field,value,negate) {
$scope.panel.query = add_to_query($scope.panel.query,field,value,negate)
var query = field+":"+angular.toJson(value)
filterSrv.set({type:'querystring',query:query,mandate:(negate ? 'mustNot':'must')})
$scope.panel.offset = 0;
$scope.get_data();
eventBus.broadcast($scope.$id,$scope.panel.group,'query',[$scope.panel.query]);
dashboard.refresh();
}
$scope.get_data = function(segment,query_id) {
$scope.panel.error = false;
// Make sure we have everything for the request to complete
if(_.isUndefined($scope.index) || _.isUndefined($scope.time))
if(dashboard.indices.length == 0) {
return
}
$scope.panel.loading = true;
var _segment = _.isUndefined(segment) ? 0 : segment
$scope.segment = _segment;
var request = $scope.ejs.Request().indices($scope.index[_segment])
.query(ejs.FilteredQuery(
ejs.QueryStringQuery($scope.panel.query || '*'),
ejs.RangeFilter($scope.time.field)
.from($scope.time.from)
.to($scope.time.to)
)
)
var request = $scope.ejs.Request().indices(dashboard.indices[_segment])
var boolQuery = ejs.BoolQuery();
_.each(query.list,function(q) {
boolQuery = boolQuery.should(ejs.QueryStringQuery(q.query || '*'))
})
request = request.query(
ejs.FilteredQuery(
boolQuery,
filterSrv.getBoolFilter(filterSrv.ids)
))
.highlight(
ejs.Highlight($scope.panel.highlight)
.fragmentSize(2147483647) // Max size of a 32bit unsigned int
@ -205,10 +201,10 @@ angular.module('kibana.table', [])
// If we're not sorting in reverse chrono order, query every index for
// size*pages results
// Otherwise, only get size*pages results then stop querying
if(
($scope.data.length < $scope.panel.size*$scope.panel.pages ||
!(($scope.panel.sort[0] === $scope.time.field) && $scope.panel.sort[1] === 'desc')) &&
_segment+1 < $scope.index.length
if($scope.data.length < $scope.panel.size*$scope.panel.pages
//($scope.data.length < $scope.panel.size*$scope.panel.pages
// || !(($scope.panel.sort[0] === $scope.time.field) && $scope.panel.sort[1] === 'desc'))
&& _segment+1 < dashboard.indices.length
) {
$scope.get_data(_segment+1,$scope.query_id)
}
@ -244,7 +240,7 @@ angular.module('kibana.table', [])
});
eventBus.broadcast($scope.$id,$scope.panel.group,"table_documents",
{
query: $scope.panel.query,
query: query.list[query.ids[0]].query,
docs : _.pluck($scope.data,'_source'),
index: $scope.index
});
@ -261,12 +257,6 @@ angular.module('kibana.table', [])
}
function set_time(time) {
$scope.time = time;
$scope.index = _.isUndefined(time.index) ? $scope.index : time.index
$scope.get_data();
}
})
.filter('highlight', function() {
return function(text) {

View File

@ -8,40 +8,6 @@
<input type="text" class="input-small" ng-model="panel.timefield">
</div>
</div>
<div class="row-fluid">
<h5>Index Settings</h5>
<div ng-show="panel.index_interval != 'none'" class="row-fluid">
<div class="span12">
<p class="small">
Time stamped indices use your selected time range to create a list of
indices that match a specified timestamp pattern. This can be very
efficient for some data sets (eg, logs) For example, to match the
default logstash index pattern you might use
<code>[logstash-]YYYY.MM.DD</code>. The [] in "[logstash-]" are
important as they instruct Kibana not to treat those letters as a
pattern.
</p>
<p class="small">
See <a href="http://momentjs.com/docs/#/displaying/format/">http://momentjs.com/docs/#/displaying/format/</a>
for documentation on date formatting.
</p>
</div>
</div>
<div class="row-fluid">
<div class="span2">
<h6>Timestamp</h6><select class="input-mini" ng-model="panel.index_interval" ng-options='f for f in ["none","hour","day","week","month","year"]'></select>
</div>
<div class="span5">
<h6>Index <span ng-show="panel.index_interval != 'none'">pattern <small>Absolutes in []</small></span></h6>
<input type="text" class="input-medium" ng-model="panel.index">
</div>
<div class="span4">
<h6>Failover Index <small>If index not found</small></h6>
<input type="text" class="input-medium" ng-model="panel.defaultindex">
</div>
</div>
</div>
<div class="row-fluid">
<h5>Relative mode <small>settings</small></h5>
<div class="span6">

View File

@ -1,5 +1,5 @@
<kibana-panel ng-controller='timepicker' ng-init="init()">
<div class="row-fluid" ng-switch="panel.mode">
<div class="row-fluid" ng-switch="panel.mode" ng-show="filterSrv.idsByType('time').length > 0">
<div ng-switch-when="absolute">
<div class="span5">
<form class="nomargin">
@ -47,17 +47,27 @@
</div>
</div>
</div>
<div class="row-fluid" ng-show="filterSrv.idsByType('time').length < 1">
<div>
<div class="span11">
<h4>No time filter present</h4>
</div>
</div>
</div>
<div class="row-fluid nomargin">
<div class="span12 small">
<div class="span12 small" ng-show="filterSrv.idsByType('time').length > 0">
<a ng-click="set_mode('relative')" ng-class="{'strong': (panel.mode == 'relative')}">Relative</a> |
<a ng-click="set_mode('absolute')" ng-class="{'strong': (panel.mode == 'absolute')}">Absolute</a> |
<a ng-click="set_mode('since')" ng-class="{'strong': (panel.mode == 'since')}">Since</a>
<span ng-hide="panel.mode == 'absolute'"> |
<span ng-hide="panel.mode == 'absolute' || panel.mode == 'none'"> |
<input type="checkbox" ng-model="panel.refresh.enable" ng-change='refresh();'> Auto-refresh
<span ng-class="{'ng-cloak': !panel.refresh.enable}">
every <a data-title="<small>Auto-refresh Settings</small>" data-placement="bottom" bs-popover="'panels/timepicker/refreshctrl.html'">{{panel.refresh.interval}}s</a>.
</span>
</span>
</div>
<div class="span12 small" ng-show="filterSrv.idsByType('time').length < 1">
<a class='btn btn-small' ng-click="time_apply()">Create a time filter</a>
</div>
</div>
</kibana-panel>

View File

@ -11,24 +11,14 @@
* time_options :: An array of possible time options. Default: ['5m','15m','1h','6h','12h','24h','2d','7d','30d']
* timespan :: The default options selected for the relative view. Default: '15m'
* timefield :: The field in which time is stored in the document.
* index :: Index pattern to match. Literals should be double quoted. Default: '_all'
* defaultindex :: Index to failover to if index not found
* index_interval :: Time between timestamped indices (can be 'none') for static index
* refresh: Object containing refresh parameters
* enable :: true/false, enable auto refresh by default. Default: false
* interval :: Seconds between auto refresh. Default: 30
* min :: The lowest interval a user may set
### Group Events
#### Sends
* time :: Object Includes from, to and index
#### Receives
* get_time :: Receives an object containing a $id, broadcasts back to it.
*/
angular.module('kibana.timepicker', [])
.controller('timepicker', function($scope, eventBus, $timeout, timer, $http, kbnIndex) {
.controller('timepicker', function($scope, $rootScope, $timeout, timer, $http, dashboard, filterSrv) {
// Set and populate defaults
var _d = {
@ -37,9 +27,6 @@ angular.module('kibana.timepicker', [])
time_options : ['5m','15m','1h','6h','12h','24h','2d','7d','30d'],
timespan : '15m',
timefield : '@timestamp',
index : '_all',
defaultindex : "_all",
index_interval: "none",
timeformat : "",
group : "default",
refresh : {
@ -57,6 +44,7 @@ angular.module('kibana.timepicker', [])
// Private refresh interval that we can use for view display without causing
// unnecessary refreshes during changes
$scope.refresh_interval = $scope.panel.refresh.interval
$scope.filterSrv = filterSrv;
// Init a private time object with Date() objects depending on mode
switch($scope.panel.mode) {
@ -80,41 +68,33 @@ angular.module('kibana.timepicker', [])
break;
}
$scope.time.field = $scope.panel.timefield;
$scope.time_apply();
// These 3 statements basicly do everything time_apply() does
set_timepicker($scope.time.from,$scope.time.to)
update_panel()
set_time_filter($scope.time)
dashboard.refresh();
// Start refresh timer if enabled
if ($scope.panel.refresh.enable)
$scope.set_interval($scope.panel.refresh.interval);
// In the case that a panel is not ready to receive a time event, it may
// request one be sent by broadcasting a 'get_time' with its _id to its group
// This panel can handle multiple groups
eventBus.register($scope,"get_time", function(event,id) {
eventBus.broadcast($scope.$id,id,'time',compile_time($scope.time))
});
// In case some other panel broadcasts a time, set us to an absolute range
eventBus.register($scope,"set_time", function(event,time) {
$scope.panel.mode = 'absolute';
set_timepicker(moment(time.from),moment(time.to))
$scope.time_apply()
});
$scope.$on('refresh', function() {
if(filterSrv.idsByType('time').length > 0) {
var time = filterSrv.timeRange('min')
eventBus.register($scope,"zoom", function(event,factor) {
var _timespan = ($scope.time.to.valueOf() - $scope.time.from.valueOf());
try {
if($scope.panel.mode != 'absolute') {
$scope.panel.mode = 'since'
set_timepicker(moment($scope.time.to.valueOf() - _timespan*factor),$scope.time.to)
} else {
var _center = $scope.time.to.valueOf() - _timespan/2
set_timepicker(moment(_center - (_timespan*factor)/2),
moment(_center + (_timespan*factor)/2))
if($scope.time.from.diff(moment.utc(time.from)) != 0
|| $scope.time.to.diff(moment.utc(time.to)) != 0)
{
$scope.set_mode('absolute');
// These 3 statements basicly do everything time_apply() does
set_timepicker(moment(time.from),moment(time.to))
$scope.time = $scope.time_calc();
update_panel()
}
} catch (e) {
console.log(e)
}
$scope.time_apply();
});
}
@ -146,10 +126,26 @@ angular.module('kibana.timepicker', [])
}
}
var update_panel = function() {
// Update panel's string representation of the time object.Don't update if
// we're in relative mode since we dont want to store the time object in the
// json for relative periods
if($scope.panel.mode !== 'relative') {
$scope.panel.time = {
from : $scope.time.from.format("MM/DD/YYYY HH:mm:ss"),
to : $scope.time.to.format("MM/DD/YYYY HH:mm:ss"),
};
} else {
delete $scope.panel.time;
}
}
$scope.set_mode = function(mode) {
$scope.panel.mode = mode;
$scope.panel.refresh.enable = mode === 'absolute' ?
false : $scope.panel.refresh.enable
update_panel();
}
$scope.to_now = function() {
@ -204,51 +200,40 @@ angular.module('kibana.timepicker', [])
$scope.time_apply = function() {
$scope.panel.error = "";
// Update internal time object
// Remove all other time filters
filterSrv.removeByType('time')
$scope.time = $scope.time_calc();
$scope.time.field = $scope.panel.timefield
update_panel()
// Get indices for the time period, then broadcast time range and index list
// in a single object. Not sure if I like this.
if($scope.panel.index_interval !== 'none') {
kbnIndex.indices($scope.time.from,
$scope.time.to,
$scope.panel.index,
$scope.panel.index_interval
).then(function (p) {
if(p.length > 0) {
$scope.time.index = p;
eventBus.broadcast($scope.$id,$scope.panel.group,'time',compile_time($scope.time))
} else {
$scope.panel.error = "Could not match index pattern to any ElasticSearch indices"
}
});
} else {
$scope.time.index = [$scope.panel.index];
eventBus.broadcast($scope.$id,$scope.panel.group,'time',compile_time($scope.time))
}
set_time_filter($scope.time)
dashboard.refresh();
// Update panel's string representation of the time object.Don't update if
// we're in relative mode since we dont want to store the time object in the
// json for relative periods
if($scope.panel.mode !== 'relative') {
$scope.panel.time = {
from : $scope.time.from.format("MM/DD/YYYY HH:mm:ss"),
to : $scope.time.to.format("MM/DD/YYYY HH:mm:ss"),
index : $scope.time.index,
};
} else {
delete $scope.panel.time;
}
};
// Prefer to pass around Date() objects in the EventBus since interacting with
function set_time_filter(time) {
time.type = 'time'
// Check if there's a time filter we remember, if not, set one and remember it
if(!_.isUndefined($scope.panel.filter_id) &&
!_.isUndefined(filterSrv.list[$scope.panel.filter_id]) &&
filterSrv.list[$scope.panel.filter_id].type == 'time')
{
filterSrv.set(compile_time(time),$scope.panel.filter_id)
} else {
$scope.panel.filter_id = filterSrv.set(compile_time(time))
}
return $scope.panel.filter_id;
}
// Prefer to pass around Date() objects since interacting with
// moment objects in libraries that are expecting Date()s can be tricky
function compile_time(time) {
time = _.clone(time)
time.from = time.from.toDate()
time.to = time.to.toDate()
time.interval = $scope.panel.index_interval
time.pattern = $scope.panel.index
return time;
}

View File

@ -26,36 +26,4 @@
<select class="input-small" ng-model="panel.arrangement" ng-options="f for f in ['horizontal','vertical']"></select></span>
</div>
</div>
<h5>Queries</h5>
<div class="row-fluid">
<div class="span3">
<form style="margin-bottom: 0px">
<label class="small">Label</label>
<input type="text" placeholder="New Label" style="width:70%" ng-model="newlabel">
</form>
</div>
<div class="span8">
<form class="input-append" style="margin-bottom: 0px">
<label class="small">Query</label>
<input type="text" placeholder="New Query" style="width:80%" ng-model="newquery">
<button class="btn" ng-click="add_query(newlabel,newquery);newlabel='';newquery='';set_refresh(true)"><i class="icon-plus"></i></button>
</form>
</div>
<div class="span1">
</div>
</div>
<div class="row-fluid" ng-repeat="q in panel.query">
<div class="span3">
<form style="margin-bottom: 0px">
<input type="text" style="width:70%" ng-model="q.label" ng-change="set_refresh(true)">
</form>
</div>
<div class="span8">
<input type="text" style="width:80%" ng-model="q.query" ng-change="set_refresh(true)">
</div>
<div class="span1">
<i class="icon-remove pointer" ng-click="remove_query(q)"></i>
</div>
</div>
</div>

View File

@ -1,10 +1,11 @@
<kibana-panel ng-controller='trends' ng-init="init()">
<div ng-style="panel.style" style="line-height:{{panel.style['font-size']}};display:inline-block;padding-right: 5px;" ng-repeat="query in trends">
<i class="icon-circle" style="color:{{query.info.color}}"></i>
<span ng-class="{'text-success': query.hits.new >= query.hits.old, 'text-error': query.hits.old > query.hits.new}" class='strong'>
<i class='large' ng-class="{'icon-caret-up': query.hits.new >= query.hits.old, 'icon-caret-down': query.hits.old > query.hits.new}"></i> {{query.percent}}%
</span>
<span class="tiny pointer light" bs-tooltip="'Then: '+query.hits.old+', Now: '+query.hits.new">({{query.label}})</span>
<span class="tiny pointer light" bs-tooltip="'Then: '+query.hits.old+', Now: '+query.hits.new" ng-show="query.info.alias != ''">({{query.info.alias}})</span>
<br ng-show="panel.arrangement == 'vertical'">
</div>
</kibana-panel>

View File

@ -19,7 +19,7 @@
*/
angular.module('kibana.trends', [])
.controller('trends', function($scope, eventBus, kbnIndex) {
.controller('trends', function($scope, kbnIndex, query, dashboard, filterSrv) {
// Set and populate defaults
var _d = {
@ -34,17 +34,10 @@ angular.module('kibana.trends', [])
$scope.init = function () {
$scope.hits = 0;
eventBus.register($scope,'time', function(event,time){
set_time(time)
});
eventBus.register($scope,'query', function(event, query) {
$scope.panel.query = _.map(query,function(q) {
return {query: q, label: q};
})
$scope.$on('refresh',function(){$scope.get_data()})
$scope.get_data();
});
// Now that we're all setup, request the time from our group
eventBus.broadcast($scope.$id,$scope.panel.group,'get_time')
}
$scope.get_data = function(segment,query_id) {
@ -52,8 +45,11 @@ angular.module('kibana.trends', [])
$scope.panel.loading = true;
// Make sure we have everything for the request to complete
if(_.isUndefined($scope.index) || _.isUndefined($scope.time))
if(dashboard.indices.length == 0) {
return
} else {
$scope.index = dashboard.indices
}
$scope.old_time = {
from : new Date($scope.time.from.getTime() - interval_to_seconds($scope.panel.ago)*1000),
@ -64,43 +60,31 @@ angular.module('kibana.trends', [])
var request = $scope.ejs.Request();
// Build the question part of the query
var queries = [];
_.each($scope.panel.query, function(v) {
queries.push($scope.ejs.FilteredQuery(
ejs.QueryStringQuery(v.query || '*'),
ejs.RangeFilter($scope.time.field)
.from($scope.time.from)
.to($scope.time.to))
)
_.each(query.ids, function(id) {
var q = $scope.ejs.FilteredQuery(
ejs.QueryStringQuery(query.list[id].query || '*'),
filterSrv.getBoolFilter(filterSrv.ids))
request = request
.facet($scope.ejs.QueryFacet(id)
.query(q)
).size(0)
});
// Build the facet part
_.each(queries, function(v) {
request = request
.facet($scope.ejs.QueryFacet("new"+_.indexOf(queries,v))
.query(v)
).size(0)
})
var queries = [];
_.each($scope.panel.query, function(v) {
queries.push($scope.ejs.FilteredQuery(
ejs.QueryStringQuery(v.query || '*'),
// And again for the old time period
_.each(query.ids, function(id) {
var q = $scope.ejs.FilteredQuery(
ejs.QueryStringQuery(query.list[id].query || '*'),
ejs.RangeFilter($scope.time.field)
.from($scope.old_time.from)
.to($scope.old_time.to))
)
request = request
.facet($scope.ejs.QueryFacet("old_"+id)
.query(q)
).size(0)
});
// Build the facet part
_.each(queries, function(v) {
request = request
.facet($scope.ejs.QueryFacet("old"+_.indexOf(queries,v))
.query(v)
).size(0)
})
// TODO: Spy for hits panel
// TODO: Spy for trend panel
//$scope.populate_modal(request);
// If we're on the first segment we need to get our indices
@ -134,11 +118,19 @@ angular.module('kibana.trends', [])
$scope.panel.error = $scope.parse_error(results.error);
return;
}
if($scope.query_id === query_id) {
// Convert facet ids to numbers
var facetIds = _.map(_.keys(results.facets),function(k){if(!isNaN(k)){return parseInt(k)}})
// Make sure we're still on the same query/queries
if($scope.query_id === query_id &&
_.intersection(facetIds,query.ids).length == query.ids.length
) {
var i = 0;
_.each($scope.panel.query, function(k) {
var n = results.facets['new'+i].count
var o = results.facets['old'+i].count
_.each(query.ids, function(id) {
var v = results.facets[id]
var n = results.facets[id].count
var o = results.facets['old_'+id].count
var hits = {
new : _.isUndefined($scope.data[i]) || _segment == 0 ? n : $scope.data[i].hits.new+n,
@ -152,7 +144,7 @@ angular.module('kibana.trends', [])
'?' : Math.round(percentage(hits.old,hits.new)*100)/100
// Create series
$scope.data[i] = {
label: $scope.panel.query[i].label || "query"+(parseInt(i)+1),
info: query.list[id],
hits: {
new : hits.new,
old : hits.old
@ -177,19 +169,6 @@ angular.module('kibana.trends', [])
return x == 0 ? null : 100*(y-x)/x
}
$scope.remove_query = function(q) {
$scope.panel.query = _.without($scope.panel.query,q);
$scope.get_data();
}
$scope.add_query = function(label,query) {
$scope.panel.query.unshift({
query: query,
label: label,
});
$scope.get_data();
}
$scope.set_refresh = function (state) {
$scope.refresh = state;
}
@ -201,10 +180,4 @@ angular.module('kibana.trends', [])
$scope.$emit('render');
}
function set_time(time) {
$scope.time = time;
$scope.index = time.index || $scope.index
$scope.get_data();
}
})

View File

@ -1,8 +1,7 @@
<div class="row-fluid container" style="margin-top:10px">
<!-- Rows -->
<div ng-controller="dashcontrol" ng-init="init()"></div>
<div class="row-fluid kibana-row" ng-controller="RowCtrl" ng-repeat="(row_name, row) in dashboards.rows" ng-style="row_style(row)">
<div class="row-fluid kibana-row" ng-controller="RowCtrl" ng-repeat="(row_name, row) in dashboard.current.rows" ng-style="row_style(row)">
<div class="row-control">
<div class="row-fluid row-header" style="padding:0px;margin:0px;height:0px">
<div style="vertical-align:bottom">

View File

@ -1,38 +1,56 @@
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3>{{dashboards.title}} <small> editor</small></h3>
<h3>{{dashboard.current.title}} <small> editor</small></h3>
</div>
<div class="modal-body">
<h4>Dashboard Control</h4>
<div class="row-fluid">
<div class="span8">
<label class="small">Title</label><input type="text" class="input-large" ng-model='dashboards.title'></input>
<label class="small">Title</label><input type="text" class="input-large" ng-model='dashboard.current.title'></input>
</div>
<div class="span1">
<label class="small"> Editable </label><input type="checkbox" ng-model="dashboards.editable" ng-checked="dashboards.editable" />
<label class="small"> Editable </label><input type="checkbox" ng-model="dashboard.current.editable" ng-checked="dashboard.current.editable" />
</div>
</div>
<div class="row-fluid">
<h4>Index Settings</h4>
<div ng-show="dashboard.current.index.interval != 'none'" class="row-fluid">
<div class="span12">
<h4>Rows</h4>
<table class="table table-condensed table-striped">
<thead>
<th>Title</th>
<th>Delete</th>
<th>Move</th>
</thead>
<tr ng-repeat="row in dashboards.rows">
<td>{{row.title}}</td>
<td><i ng-click="dashboards.rows = _.without(dashboards.rows,row)" class="pointer icon-remove"></i></td>
<td><i ng-click="_.move(dashboards.rows,$index,$index-1)" ng-hide="$first" class="pointer icon-arrow-up"></i></td>
<td><i ng-click="_.move(dashboards.rows,$index,$index+1)" ng-hide="$last" class="pointer icon-arrow-down"></i></td>
</tr>
</table>
<p class="small">
Time stamped indices use your selected time range to create a list of
indices that match a specified timestamp pattern. This can be very
efficient for some data sets (eg, logs) For example, to match the
default logstash index pattern you might use
<code>[logstash-]YYYY.MM.DD</code>. The [] in "[logstash-]" are
important as they instruct Kibana not to treat those letters as a
pattern.
</p>
<p class="small">
See <a href="http://momentjs.com/docs/#/displaying/format/">http://momentjs.com/docs/#/displaying/format/</a>
for documentation on date formatting.
</p>
</div>
</div>
<h4>New row</h4>
<div class="row-fluid">
<div class="span8">
<div class="span3">
<h6>Timestamping</h6><select class="input-small" ng-model="dashboard.current.index.interval" ng-options='f for f in ["none","hour","day","week","month","year"]'></select>
</div>
<div class="span5" ng-show="dashboard.current.index.interval != 'none'">
<h6>Index pattern <small>Absolutes in []</small></h6>
<input type="text" class="input-medium" ng-model="dashboard.current.index.pattern">
</div>
<div class="span4">
<h6>Default Index <small ng-show="dashboard.current.index.interval != 'none'">If index not found</small></h6>
<input type="text" class="input-medium" ng-model="dashboard.current.index.default">
</div>
</div>
</div>
<hr/>
<h4>Rows</h4>
<div class="row-fluid">
<form>
<div class="span5">
<label class="small">Title</label>
<input type="text" class="input-large" ng-model='row.title'></input>
</div>
@ -44,10 +62,30 @@
<label class="small"> Editable </label>
<input type="checkbox" ng-model="row.editable" ng-checked="row.editable" />
</div>
<div class="span4">
<label>&nbsp</label>
<button ng-click="add_row(dashboard.current,row); reset_row();" class="btn btn-primary">Create Row</button>
</div>
</form>
</div>
<div class="row-fluid">
<div class="span12">
<table class="table table-condensed table-striped">
<thead>
<th>Title</th>
<th>Delete</th>
<th>Move</th>
</thead>
<tr ng-repeat="row in dashboard.current.rows">
<td>{{row.title}}</td>
<td><i ng-click="dashboard.current.rows = _.without(dashboard.current.rows,row)" class="pointer icon-remove"></i></td>
<td><i ng-click="_.move(dashboard.current.rows,$index,$index-1)" ng-hide="$first" class="pointer icon-arrow-up"></i></td>
<td><i ng-click="_.move(dashboard.current.rows,$index,$index+1)" ng-hide="$last" class="pointer icon-arrow-down"></i></td>
</tr>
</table>
</div>
<button ng-click="add_row(dashboards,row); reset_row();" class="btn btn-primary">Create Row</button><br>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-success" ng-click="dismiss();reset_panel();">Close</button>
<button type="button" class="btn btn-success" ng-click="dismiss();reset_panel();dashboard.refresh()">Close</button>
</div>

View File

@ -2,9 +2,6 @@
<div class="span4">
<label class="small">Title</label><input type="text" class="input-medium" ng-model='panel.title'></input>
</div>
<div class="span4">
<label class="small">Group(s) (comma seperated)</label><input array-join type="text" class="input-medium" ng-model='panel.group'></input>
</div>
<div class="span2" ng-hide="panel.sizeable == false">
<label class="small">Span</label> <select class="input-mini" ng-model="panel.span" ng-options="f for f in [0,1,2,3,4,5,6,7,8,9,10,11,12]"></select>
</div>