mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge pull request #219 from rashidkpc/query_service
Refactor of queries, filters, dashboard service
This commit is contained in:
commit
fdb3492825
12
README.md
12
README.md
@ -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
9
common/css/bootstrap.light.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
858
common/css/bootstrap.min.css
vendored
858
common/css/bootstrap.min.css
vendored
@ -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
@ -1,6 +0,0 @@
|
||||
.axis path,
|
||||
.axis line {
|
||||
fill: none;
|
||||
stroke: #000;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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%;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/")};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);
|
@ -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'],
|
||||
}
|
||||
);
|
||||
|
@ -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-*"
|
||||
}
|
||||
}
|
11
index.html
11
index.html
@ -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>
|
||||
|
@ -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'
|
||||
|
@ -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() {
|
||||
|
@ -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',
|
||||
|
587
js/services.js
587
js/services.js
@ -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 = [];
|
||||
|
@ -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>
|
||||
|
@ -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 {
|
||||
|
@ -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>
|
@ -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>
|
||||
|
@ -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>
|
@ -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);
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
@ -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) {
|
||||
|
@ -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>
|
||||
|
@ -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) {
|
||||
|
7
panels/filtering/editor.html
Normal file
7
panels/filtering/editor.html
Normal file
@ -0,0 +1,7 @@
|
||||
<div>
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
No options here
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
15
panels/filtering/meta.html
Normal file
15
panels/filtering/meta.html
Normal 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>
|
45
panels/filtering/module.html
Normal file
45
panels/filtering/module.html
Normal 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>
|
46
panels/filtering/module.js
Normal file
46
panels/filtering/module.js
Normal 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)
|
||||
}
|
||||
|
||||
});
|
@ -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>
|
||||
|
@ -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>
|
@ -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();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -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>
|
||||
|
@ -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>
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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>
|
@ -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();
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
@ -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();
|
||||
|
||||
|
||||
}
|
@ -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();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@ -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>
|
1
panels/map2/lib/d3.hexbin.v0.min.js
vendored
1
panels/map2/lib/d3.hexbin.v0.min.js
vendored
@ -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]}})();
|
@ -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;
|
1
panels/map2/lib/queue.v1.min.js
vendored
1
panels/map2/lib/queue.v1.min.js
vendored
@ -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})();
|
1
panels/map2/lib/topojson.v1.min.js
vendored
1
panels/map2/lib/topojson.v1.min.js
vendored
@ -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
@ -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,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>
|
@ -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 + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
@ -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>
|
||||
|
@ -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>
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
});
|
@ -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>
|
@ -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
7
panels/query/editor.html
Normal 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
15
panels/query/meta.html
Normal 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
51
panels/query/module.html
Normal 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
60
panels/query/module.js
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
});
|
@ -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>
|
@ -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();
|
||||
}
|
||||
})
|
@ -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>
|
@ -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>
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
});
|
@ -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">
|
||||
|
@ -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) {
|
||||
|
@ -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">
|
||||
|
@ -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>
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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>
|
@ -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();
|
||||
}
|
||||
|
||||
})
|
@ -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">
|
||||
|
@ -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> </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>
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user