diff --git a/common/css/datepicker.css b/common/css/datepicker.css
deleted file mode 100644
index 15268d94bb7..00000000000
--- a/common/css/datepicker.css
+++ /dev/null
@@ -1,156 +0,0 @@
-/* =========================================================
- * bootstrap-datepicker.js
- * original by Stefan Petre
- * tweaked by gus
- * ========================================================= */
-
-.bs-sc-datepicker.dropdown-menu {
-max-width: inherit;
-}
-.bs-sc-datepicker {
-top: 0;
-left: 0;
-padding: 4px;
-margin-top: 1px;
--webkit-border-radius: 4px;
--moz-border-radius: 4px;
-border-radius: 4px;
-z-index: 1051;
-}
-.bs-sc-datepicker: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: 6px;
-}
-.bs-sc-datepicker: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: 7px;
-}
-.bs-sc-datepicker table {
-width: 100%;
-margin: 0;
-}
-.bs-sc-datepicker th {
-border-bottom: 1px solid #efefef;
-}
-.bs-sc-datepicker td, .bs-sc-datepicker th {
-text-align: center;
-width: 20px;
-height: 20px;
--webkit-border-radius: 4px;
--moz-border-radius: 4px;
-border-radius: 4px;
-padding: 5px !important;
-}
-.bs-sc-datepicker td.day:hover {
-background: #eeeeee;
-cursor: pointer;
-}
-.bs-sc-datepicker td.old, .bs-sc-datepicker td.new {
-color: #DDDDDD;
-}
-.bs-sc-datepicker td.active, .bs-sc-datepicker td.active:hover {
-background-color: #006dcc;
-background-image: -moz-linear-gradient(top, #0088cc, #0044cc);
-background-image: -ms-linear-gradient(top, #0088cc, #0044cc);
-background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));
-background-image: -webkit-linear-gradient(top, #0088cc, #0044cc);
-background-image: -o-linear-gradient(top, #0088cc, #0044cc);
-background-image: linear-gradient(top, #0088cc, #0044cc);
-background-repeat: repeat-x;
-filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0);
-border-color: #0044cc #0044cc #002a80;
-border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
-filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
-color: #fff;
-text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
-}
-.bs-sc-datepicker td.active:hover,
-.bs-sc-datepicker td.active:hover:hover,
-.bs-sc-datepicker td.active:active,
-.bs-sc-datepicker td.active:hover:active,
-.bs-sc-datepicker td.active.active,
-.bs-sc-datepicker td.active:hover.active,
-.bs-sc-datepicker td.active.disabled,
-.bs-sc-datepicker td.active:hover.disabled,
-.bs-sc-datepicker td.active[disabled],
-.bs-sc-datepicker td.active:hover[disabled] {
-background-color: #0044cc;
-}
-.bs-sc-datepicker td.active:active,
-.bs-sc-datepicker td.active:hover:active,
-.bs-sc-datepicker td.active.active,
-.bs-sc-datepicker td.active:hover.active {
-background-color: #003399 \9;
-}
-.bs-sc-datepicker td span {
-display: block;
-width: 47px;
-height: 54px;
-line-height: 54px;
-float: left;
-margin: 2px;
-cursor: pointer;
--webkit-border-radius: 4px;
--moz-border-radius: 4px;
-border-radius: 4px;
-}
-.bs-sc-datepicker td span:hover {
-background: #eeeeee;
-}
-.bs-sc-datepicker td span.active {
-background-color: #006dcc;
-background-image: -moz-linear-gradient(top, #0088cc, #0044cc);
-background-image: -ms-linear-gradient(top, #0088cc, #0044cc);
-background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));
-background-image: -webkit-linear-gradient(top, #0088cc, #0044cc);
-background-image: -o-linear-gradient(top, #0088cc, #0044cc);
-background-image: linear-gradient(top, #0088cc, #0044cc);
-background-repeat: repeat-x;
-filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0);
-border-color: #0044cc #0044cc #002a80;
-border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
-filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
-color: #fff;
-text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
-}
-.bs-sc-datepicker td span.active:hover,
-.bs-sc-datepicker td span.active:active,
-.bs-sc-datepicker td span.active.active,
-.bs-sc-datepicker td span.active.disabled,
-.bs-sc-datepicker td span.active[disabled] {
-background-color: #0044cc;
-}
-.bs-sc-datepicker td span.active:active, .bs-sc-datepicker td span.active.active {
-background-color: #003399 \9;
-}
-.bs-sc-datepicker td span.old {
-color: #999999;
-}
-.bs-sc-datepicker th.switch {
-width: 145px;
-}
-.bs-sc-datepicker thead tr:first-child th {
-cursor: pointer;
-}
-.bs-sc-datepicker thead tr:first-child th:hover {
-background: #eeeeee;
-}
-.input-append.date .add-on i, .input-prepend.date .add-on i {
-display: block;
-cursor: pointer;
-width: 16px;
-height: 16px;
-}
\ No newline at end of file
diff --git a/common/css/main.css b/common/css/main.css
index a2ec63275e0..f3a182ef850 100644
--- a/common/css/main.css
+++ b/common/css/main.css
@@ -22,4 +22,33 @@
.pointer {
cursor: pointer;
+}
+
+.small {
+ font-size: 85%;
+}
+
+.nomargin {
+ margin: 0px;
+}
+
+.strong {
+ font-weight: bold;
+}
+
+.btn-active {
+ background-color: #E6E6E6;
+ background-image: none;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15) inset, 0 1px 2px rgba(0, 0, 0, 0.05);
+ outline: 0 none
+}
+
+.popover-title { display: none; }
+
+.input-smaller {
+ width: 75px;
+}
+
+.tiny {
+ font-size: 50%;
}
\ No newline at end of file
diff --git a/common/css/timepicker.css b/common/css/timepicker.css
new file mode 100644
index 00000000000..8166d4da0e9
--- /dev/null
+++ b/common/css/timepicker.css
@@ -0,0 +1,91 @@
+
+/*
+ Datepicker for Bootstrap
+ Copyright 2012 Stefan Petre
+ Licensed under the Apache License v2.0
+ http://www.apache.org/licenses/LICENSE-2.0
+*/
+input[type="date"] { -webkit-appearance: none; } .datepicker { top: 0; left: 0; padding: 4px; margin-top: 1px; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; /*.dow { border-top: 1px solid #ddd !important; }*/ } .datepicker: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: 6px; } .datepicker: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: 7px; } .datepicker > div { display: none; } .datepicker table { width: 100%; margin: 0; } .datepicker td, .datepicker th { text-align: center; width: 20px; height: 20px; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; } .datepicker td.day:hover { background: #eeeeee; cursor: pointer; } .datepicker td.old, .datepicker td.new { color: #999999; } .datepicker td.active, .datepicker td.active:hover { background-color: #006dcc; background-image: -moz-linear-gradient(top, #0088cc, #0044cc); background-image: -ms-linear-gradient(top, #0088cc, #0044cc); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc)); background-image: -webkit-linear-gradient(top, #0088cc, #0044cc); background-image: -o-linear-gradient(top, #0088cc, #0044cc); background-image: linear-gradient(top, #0088cc, #0044cc); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0); border-color: #0044cc #0044cc #002a80; border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); color: #fff; text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); } .datepicker td.active:hover, .datepicker td.active:hover:hover, .datepicker td.active:active, .datepicker td.active:hover:active, .datepicker td.active.active, .datepicker td.active:hover.active, .datepicker td.active.disabled, .datepicker td.active:hover.disabled, .datepicker td.active[disabled], .datepicker td.active:hover[disabled] { background-color: #0044cc; } .datepicker td.active:active, .datepicker td.active:hover:active, .datepicker td.active.active, .datepicker td.active:hover.active { background-color: #003399 \9; } .datepicker td span { display: block; width: 47px; height: 54px; line-height: 54px; float: left; margin: 2px; cursor: pointer; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; } .datepicker td span:hover { background: #eeeeee; } .datepicker td span.active { background-color: #006dcc; background-image: -moz-linear-gradient(top, #0088cc, #0044cc); background-image: -ms-linear-gradient(top, #0088cc, #0044cc); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc)); background-image: -webkit-linear-gradient(top, #0088cc, #0044cc); background-image: -o-linear-gradient(top, #0088cc, #0044cc); background-image: linear-gradient(top, #0088cc, #0044cc); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0); border-color: #0044cc #0044cc #002a80; border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); color: #fff; text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); } .datepicker td span.active:hover, .datepicker td span.active:active, .datepicker td span.active.active, .datepicker td span.active.disabled, .datepicker td span.active[disabled] { background-color: #0044cc; } .datepicker td span.active:active, .datepicker td span.active.active { background-color: #003399 \9; } .datepicker td span.old { color: #999999; } .datepicker th.switch { width: 145px; } .datepicker th.next, .datepicker th.prev { font-size: 19.5px; } .datepicker thead tr:first-child th { cursor: pointer; } .datepicker thead tr:first-child th:hover { background: #eeeeee; } .input-append.date .add-on i, .input-prepend.date .add-on i { display: block; cursor: pointer; width: 16px; height: 16px; }
+
+.bootstrap-timepicker.dropdown-menu {
+ border-radius: 4px 4px 4px 4px;
+ display: none;
+ left: 0;
+ margin-top: 1px;
+ padding: 4px;
+ top: 0;
+ min-width: 10px;
+ z-index: 99999;
+}
+.bootstrap-timepicker.dropdown-menu.open {
+ display: inline-block;
+}
+.bootstrap-timepicker.dropdown-menu:before {
+ border-bottom: 7px solid rgba(0, 0, 0, 0.2);
+ border-left: 7px solid transparent;
+ border-right: 7px solid transparent;
+ content: "";
+ left: 6px;
+ position: absolute;
+ top: -7px;
+}
+.bootstrap-timepicker.dropdown-menu:after {
+ border-bottom: 6px solid #FFFFFF;
+ border-left: 6px solid transparent;
+ border-right: 6px solid transparent;
+ content: "";
+ left: 7px;
+ position: absolute;
+ top: -6px;
+}
+.bootstrap-timepicker.modal {
+ margin-left: -100px;
+ margin-top: 0;
+ top: 30%;
+ width: 200px;
+}
+.bootstrap-timepicker.modal .modal-content {
+ padding: 0;
+}
+.bootstrap-timepicker table {
+ margin: 0;
+ width: 100%;
+}
+.bootstrap-timepicker table td {
+ height: 30px;
+ margin: 0;
+ padding: 2px;
+ text-align: center;
+}
+.bootstrap-timepicker table td span {
+ width: 100%;
+}
+.bootstrap-timepicker table td a {
+ border: 1px solid transparent;
+ display: inline-block;
+ margin: 0;
+ outline: 0 none;
+ padding: 8px 0;
+ width: 3em;
+}
+.bootstrap-timepicker table td a:hover {
+ background-color: #EEEEEE;
+ border-color: #DDDDDD;
+ border-radius: 4px 4px 4px 4px;
+}
+.bootstrap-timepicker table td a i {
+ margin-top: 2px;
+}
+.bootstrap-timepicker table td input {
+ margin: 0;
+ text-align: center;
+ width: 25px;
+}
+.bootstrap-timepicker-component .add-on {
+ cursor: pointer;
+}
+.bootstrap-timepicker-component .add-on i {
+ display: block;
+ height: 16px;
+ width: 16px;
+}
diff --git a/common/lib/datepicker.js b/common/lib/datepicker.js
index 671bbbf9eed..6c97ff522c2 100644
--- a/common/lib/datepicker.js
+++ b/common/lib/datepicker.js
@@ -1,188 +1,377 @@
/* =========================================================
* bootstrap-datepicker.js
- * original by Stefan Petre
- * tweaked by gus
+ * http://www.eyecon.ro/bootstrap-datepicker
+ * =========================================================
+ * Copyright 2012 Stefan Petre
+ * Improvements by Andrew Rowls
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
* ========================================================= */
!function( $ ) {
+ function UTCDate(){
+ return new Date(Date.UTC.apply(Date, arguments));
+ }
+ function UTCToday(){
+ var today = new Date();
+ return UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
+ }
+
// Picker object
- var Datepicker = function(element, options){
+ var Datepicker = function(element, options) {
+ var that = this;
+
this.element = $(element);
+ this.language = options.language||this.element.data('date-language')||"en";
+ this.language = this.language in dates ? this.language : "en";
+ this.format = DPGlobal.parseFormat(options.format||this.element.data('date-format')||'mm/dd/yyyy');
+ this.picker = $(DPGlobal.template)
+ .appendTo('body')
+ .on({
+ click: $.proxy(this.click, this)
+ });
+ this.isInput = this.element.is('input');
+ this.component = this.element.is('.date') ? this.element.find('.add-on') : false;
+ this.hasInput = this.component && this.element.find('input').length;
+ if(this.component && this.component.length === 0)
+ this.component = false;
- this.days = options.days||["sun","mon","tue","wed","thu","fri","sat"];
- this.months = options.months||["january","february","march","april","may","june","july","august","september","october","november","december"];
- this.format = options.format||$(element).data("datepicker-format")||'mm/dd/yyyy hh:ii:ss';
- this.noDefault = options.noDefault||$(element).data("datepicker-nodefault")||false;
+ if (this.isInput) {
+ this.element.on({
+ focus: $.proxy(this.show, this),
+ keyup: $.proxy(this.update, this),
+ keydown: $.proxy(this.keydown, this)
+ });
+ } else {
+ if (this.component && this.hasInput){
+ // For components that are not readonly, allow keyboard nav
+ this.element.find('input').on({
+ focus: $.proxy(this.show, this),
+ keyup: $.proxy(this.update, this),
+ keydown: $.proxy(this.keydown, this)
+ });
- this.picker = $(DPGlobal.template).appendTo("body").on({
- mousedown: $.proxy(this.click, this)
- });
-
- this.weekStart = options.weekStart||0;
- this.weekEnd = this.weekStart == 0 ? 6 : this.weekStart - 1;
- this.head();
-
- if (!this.element.prop("value")&&!this.noDefault) {
- this.element.prop("value",DPGlobal.formatDate(new Date(), this.format));
+ this.component.on('click', $.proxy(this.show, this));
+ } else {
+ this.element.on('click', $.proxy(this.show, this));
+ }
}
- this.update();
-
- this.element.on({
- focus: $.proxy(this.show, this),
- click: $.proxy(this.show, this),
- keyup: $.proxy(this.keyup, this)
+ $(document).on('mousedown', function (e) {
+ // Clicked outside the datepicker, hide it
+ if ($(e.target).closest('.datepicker').length == 0) {
+ that.hide();
+ }
});
+
+ this.autoclose = false;
+ if ('autoclose' in options) {
+ this.autoclose = options.autoclose;
+ } else if ('dateAutoclose' in this.element.data()) {
+ this.autoclose = this.element.data('date-autoclose');
+ }
+
+ this.keyboardNavigation = true;
+ if ('keyboardNavigation' in options) {
+ this.keyboardNavigation = options.keyboardNavigation;
+ } else if ('dateKeyboardNavigation' in this.element.data()) {
+ this.keyboardNavigation = this.element.data('date-keyboard-navigation');
+ }
+
+ switch(options.startView || this.element.data('date-start-view')){
+ case 2:
+ case 'decade':
+ this.viewMode = this.startViewMode = 2;
+ break;
+ case 1:
+ case 'year':
+ this.viewMode = this.startViewMode = 1;
+ break;
+ case 0:
+ case 'month':
+ default:
+ this.viewMode = this.startViewMode = 0;
+ break;
+ }
+
+ this.todayBtn = (options.todayBtn||this.element.data('date-today-btn')||false);
+ this.todayHighlight = (options.todayHighlight||this.element.data('date-today-highlight')||false);
+
+ this.weekStart = ((options.weekStart||this.element.data('date-weekstart')||dates[this.language].weekStart||0) % 7);
+ this.weekEnd = ((this.weekStart + 6) % 7);
+ this.startDate = -Infinity;
+ this.endDate = Infinity;
+ this.setStartDate(options.startDate||this.element.data('date-startdate'));
+ this.setEndDate(options.endDate||this.element.data('date-enddate'));
+ this.fillDow();
+ this.fillMonths();
+ this.update();
+ this.showMode();
};
Datepicker.prototype = {
constructor: Datepicker,
show: function(e) {
- this.update();
this.picker.show();
- this.height = this.element.outerHeight();
+ this.height = this.component ? this.component.outerHeight() : this.element.outerHeight();
+ this.update();
this.place();
- $(window).on("resize", $.proxy(this.place, this));
- if (e) {
+ $(window).on('resize', $.proxy(this.place, this));
+ if (e ) {
e.stopPropagation();
e.preventDefault();
}
this.element.trigger({
- type: "show",
+ type: 'show',
date: this.date
});
- $("body").on("click.bs-sc-datepicker", $.proxy(this.hide, this));
},
hide: function(e){
- if (e && $(e.target).parents(".bs-sc-datepicker").length) return false;
this.picker.hide();
- $(window).off("resize", this.place);
- $("body").off("click.bs-sc-datepicker");
+ $(window).off('resize', this.place);
+ this.viewMode = this.startViewMode;
+ this.showMode();
+ if (!this.isInput) {
+ $(document).off('mousedown', this.hide);
+ }
+ if (e && e.currentTarget.value)
+ this.setValue();
+ this.element.trigger({
+ type: 'hide',
+ date: this.date
+ });
},
- setValue: function(val) {
- if (typeof(val)!=='undefined') {
- this.date = val;
+ getDate: function() {
+ var d = this.getUTCDate();
+ return new Date(d.getTime() + (d.getTimezoneOffset()*60000))
+ },
+
+ getUTCDate: function() {
+ return this.date;
+ },
+
+ setDate: function(d) {
+ this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
+ },
+
+ setUTCDate: function(d) {
+ this.date = d;
+ this.setValue();
+ },
+
+ setValue: function() {
+ var formatted = DPGlobal.formatDate(this.date, this.format, this.language);
+ if (!this.isInput) {
+ if (this.component){
+ this.element.find('input').prop('value', formatted);
+ }
+ this.element.data('date', formatted);
+ } else {
+ this.element.prop('value', formatted);
}
- var formated = DPGlobal.formatDate(this.date, this.format);
- this.element.prop("value", formated);
+ },
+
+ setStartDate: function(startDate){
+ this.startDate = startDate||-Infinity;
+ if (this.startDate !== -Infinity) {
+ this.startDate = DPGlobal.parseDate(this.startDate, this.format, this.language);
+ }
+ this.update();
+ this.updateNavArrows();
+ },
+
+ setEndDate: function(endDate){
+ this.endDate = endDate||Infinity;
+ if (this.endDate !== Infinity) {
+ this.endDate = DPGlobal.parseDate(this.endDate, this.format, this.language);
+ }
+ this.update();
+ this.updateNavArrows();
},
place: function(){
- var offset = this.element.offset();
+ var zIndex = parseInt(this.element.parents().filter(function() {
+ return $(this).css('z-index') != 'auto';
+ }).first().css('z-index'))+10;
+ var offset = this.component ? this.component.offset() : this.element.offset();
this.picker.css({
top: offset.top + this.height,
- left: offset.left
+ left: offset.left,
+ zIndex: zIndex
});
},
update: function(){
- this.date = DPGlobal.parseDate(this.element.prop("value"), this.format);
- this.viewDate = new Date(this.date);
+ this.date = DPGlobal.parseDate(
+ this.isInput ? this.element.prop('value') : this.element.data('date') || this.element.find('input').prop('value'),
+ this.format, this.language
+ );
+ if (this.date < this.startDate) {
+ this.viewDate = new Date(this.startDate);
+ } else if (this.date > this.endDate) {
+ this.viewDate = new Date(this.endDate);
+ } else {
+ this.viewDate = new Date(this.date);
+ }
this.fill();
},
- keyup: function() {
- this.date = DPGlobal.parseDate(this.element.prop("value"), this.format);
- this.element.trigger({
- type: 'changeDate',
- date: this.date
- });
- },
-
- head: function(){
+ fillDow: function(){
var dowCnt = this.weekStart;
var html = '
';
while (dowCnt < this.weekStart + 7) {
- html += ''+this.days[(dowCnt++)%7]+' | ';
+ html += ''+dates[this.language].daysMin[(dowCnt++)%7]+' | ';
}
html += '
';
- this.picker.find(".datepicker-days thead").append(html);
+ this.picker.find('.datepicker-days thead').append(html);
+ },
+
+ fillMonths: function(){
+ var html = '';
+ var i = 0
+ while (i < 12) {
+ html += ''+dates[this.language].monthsShort[i++]+'';
+ }
+ this.picker.find('.datepicker-months td').html(html);
},
fill: function() {
var d = new Date(this.viewDate),
- year = d.getFullYear(),
- month = d.getMonth(),
- day = d.getDay();
-
- currentDate = new Date(this.date.getFullYear(), this.date.getMonth(), this.date.getDate(), 0, 0, 0, 0);
- currentDate = currentDate.valueOf();
-
- if (month > 0) {
- var prevMonth = new Date(year, month-1, 1,0,0,0,0);
- var prevMonthNr = prevMonth.getMonth();
- } else {
- var prevMonth = new Date(year-1, 11, 1, 0, 0, 0, 0);
- var prevMonthNr = prevMonth.getMonth();
- }
-
- if (month < 11) {
- var nextMonthNr = month + 1;
- } else {
- var nextMonthNr = 0;
- }
-
- var beginMonth = new Date(year, month, 1,0,0,0,0);
- startAtWeekday = beginMonth.getDay() - this.weekStart;
- if (startAtWeekday < 0) {
- prevMonthDays = DPGlobal.getDaysInMonth(prevMonth.getFullYear(), prevMonth.getMonth());
- startPrevMonthAtDate = prevMonthDays - (6 + startAtWeekday);
- } else if (startAtWeekday > 0) {
- prevMonthDays = DPGlobal.getDaysInMonth(prevMonth.getFullYear(), prevMonth.getMonth());
- startPrevMonthAtDate = prevMonthDays - startAtWeekday + 1;
- } else {
- startPrevMonthAtDate = 1;
- prevMonth.setMonth(month);
- }
-
- prevMonth.setDate(startPrevMonthAtDate);
-
- d = prevMonth;
-
- html = []; allDone = false; x=0;
-
- while(!allDone) {
-
- if (d.getDay() == this.weekStart) {
-
+ year = d.getUTCFullYear(),
+ month = d.getUTCMonth(),
+ startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
+ startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
+ endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
+ endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
+ currentDate = this.date.valueOf(),
+ today = new Date();
+ this.picker.find('.datepicker-days thead th:eq(1)')
+ .text(dates[this.language].months[month]+' '+year);
+ this.picker.find('tfoot th.today')
+ .text(dates[this.language].today)
+ .toggle(this.todayBtn);
+ this.updateNavArrows();
+ this.fillMonths();
+ var prevMonth = UTCDate(year, month-1, 28,0,0,0,0),
+ day = DPGlobal.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
+ prevMonth.setUTCDate(day);
+ prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
+ var nextMonth = new Date(prevMonth);
+ nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
+ nextMonth = nextMonth.valueOf();
+ var html = [];
+ var clsName;
+ while(prevMonth.valueOf() < nextMonth) {
+ if (prevMonth.getUTCDay() == this.weekStart) {
html.push('');
}
-
clsName = '';
- if (d.getMonth() == prevMonthNr) {
+ if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
clsName += ' old';
- } else if (d.getMonth() == nextMonthNr) {
+ } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
clsName += ' new';
}
- if (d.valueOf() == currentDate) {
+ // Compare internal UTC date with local today, not UTC today
+ if (this.todayHighlight &&
+ prevMonth.getUTCFullYear() == today.getFullYear() &&
+ prevMonth.getUTCMonth() == today.getMonth() &&
+ prevMonth.getUTCDate() == today.getDate()) {
+ clsName += ' today';
+ }
+ if (prevMonth.valueOf() == currentDate) {
clsName += ' active';
}
- clsName += ' ' + d.valueOf();
- html.push('' + d.getDate() + ' | ');
-
- if (d.getDay() == this.weekEnd) {
+ if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate) {
+ clsName += ' disabled';
+ }
+ html.push(''+prevMonth.getUTCDate() + ' | ');
+ if (prevMonth.getUTCDay() == this.weekEnd) {
html.push('
');
}
+ prevMonth.setUTCDate(prevMonth.getUTCDate()+1);
+ }
+ this.picker.find('.datepicker-days tbody').empty().append(html.join(''));
+ var currentYear = this.date.getUTCFullYear();
- d.setDate(d.getDate()+1);
- allDone = ((d.getDay() == this.weekStart) && (d.getMonth() == nextMonthNr));
-
- x++;
- if (x > 99) {
- console.log("safety");
- return;
- }
+ var months = this.picker.find('.datepicker-months')
+ .find('th:eq(1)')
+ .text(year)
+ .end()
+ .find('span').removeClass('active');
+ if (currentYear == year) {
+ months.eq(this.date.getUTCMonth()).addClass('active');
+ }
+ if (year < startYear || year > endYear) {
+ months.addClass('disabled');
+ }
+ if (year == startYear) {
+ months.slice(0, startMonth).addClass('disabled');
+ }
+ if (year == endYear) {
+ months.slice(endMonth+1).addClass('disabled');
}
- this.picker.find('.datepicker-days tbody').empty().append(html.join(''));
+ html = '';
+ year = parseInt(year/10, 10) * 10;
+ var yearCont = this.picker.find('.datepicker-years')
+ .find('th:eq(1)')
+ .text(year + '-' + (year + 9))
+ .end()
+ .find('td');
+ year -= 1;
+ for (var i = -1; i < 11; i++) {
+ html += ''+year+'';
+ year += 1;
+ }
+ yearCont.html(html);
+ },
- headerStr = this.months[this.viewDate.getMonth()] + ' ' + this.viewDate.getFullYear();
- this.picker.find('.datepicker-days thead .monthname').html(headerStr);
+ updateNavArrows: function() {
+ var d = new Date(this.viewDate),
+ year = d.getUTCFullYear(),
+ month = d.getUTCMonth();
+ switch (this.viewMode) {
+ case 0:
+ if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
+ this.picker.find('.prev').css({visibility: 'hidden'});
+ } else {
+ this.picker.find('.prev').css({visibility: 'visible'});
+ }
+ if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
+ this.picker.find('.next').css({visibility: 'hidden'});
+ } else {
+ this.picker.find('.next').css({visibility: 'visible'});
+ }
+ break;
+ case 1:
+ case 2:
+ if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
+ this.picker.find('.prev').css({visibility: 'hidden'});
+ } else {
+ this.picker.find('.prev').css({visibility: 'visible'});
+ }
+ if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
+ this.picker.find('.next').css({visibility: 'hidden'});
+ } else {
+ this.picker.find('.next').css({visibility: 'visible'});
+ }
+ break;
+ }
},
click: function(e) {
@@ -190,62 +379,262 @@
e.preventDefault();
var target = $(e.target).closest('span, td, th');
if (target.length == 1) {
-
switch(target[0].nodeName.toLowerCase()) {
-
case 'th':
switch(target[0].className) {
- case 'prev':
- if (this.viewDate.getMonth() > 0) {
- this.viewDate.setMonth(this.viewDate.getMonth() - 1);
- } else {
- this.viewDate.setFullYear(this.viewDate.getFullYear() - 1);
- this.viewDate.setMonth(11);
- }
+ case 'switch':
+ this.showMode(1);
break;
-
+ case 'prev':
case 'next':
- if (this.viewDate.getMonth() < 11) {
- this.viewDate.setMonth(this.viewDate.getMonth() + 1);
- } else {
- this.viewDate.setFullYear(this.viewDate.getFullYear() + 1);
- this.viewDate.setMonth(0);
+ var dir = DPGlobal.modes[this.viewMode].navStep * (target[0].className == 'prev' ? -1 : 1);
+ switch(this.viewMode){
+ case 0:
+ this.viewDate = this.moveMonth(this.viewDate, dir);
+ break;
+ case 1:
+ case 2:
+ this.viewDate = this.moveYear(this.viewDate, dir);
+ break;
}
+ this.fill();
+ break;
+ case 'today':
+ var date = new Date();
+ date.setUTCHours(0);
+ date.setUTCMinutes(0);
+ date.setUTCSeconds(0);
+ date.setUTCMilliseconds(0);
+ this.showMode(-2);
+ var which = this.todayBtn == 'linked' ? null : 'view';
+ this._setDate(date, which);
break;
}
- this.fill();
+ break;
+ case 'span':
+ if (!target.is('.disabled')) {
+ this.viewDate.setUTCDate(1);
+ if (target.is('.month')) {
+ var month = target.parent().find('span').index(target);
+ this.viewDate.setUTCMonth(month);
+ this.element.trigger({
+ type: 'changeMonth',
+ date: this.viewDate
+ });
+ } else {
+ var year = parseInt(target.text(), 10)||0;
+ this.viewDate.setUTCFullYear(year);
+ this.element.trigger({
+ type: 'changeYear',
+ date: this.viewDate
+ });
+ }
+ this.showMode(-1);
+ this.fill();
+ }
break;
case 'td':
- if (target.is('.day')){
- if (target.is('.old')) {
- return;
- } else if (target.is('.new')) {
- return;
- }
-
+ if (target.is('.day') && !target.is('.disabled')){
var day = parseInt(target.text(), 10)||1;
- var month = this.viewDate.getMonth();
- var year = this.viewDate.getFullYear();
- this.date = new Date(year, month, day,this.date.getHours(),this.date.getMinutes(),this.date.getSeconds(),0);
- this.viewDate = new Date(year, month, day,0,0,0,0);
- this.fill();
- this.setValue();
- this.element.trigger({
- type: 'changeDate',
- date: this.date
- });
- this.hide();
+ var year = this.viewDate.getUTCFullYear(),
+ month = this.viewDate.getUTCMonth();
+ if (target.is('.old')) {
+ if (month == 0) {
+ month = 11;
+ year -= 1;
+ } else {
+ month -= 1;
+ }
+ } else if (target.is('.new')) {
+ if (month == 11) {
+ month = 0;
+ year += 1;
+ } else {
+ month += 1;
+ }
+ }
+ this._setDate(UTCDate(year, month, day,0,0,0,0));
}
break;
}
}
- return false;
},
+ _setDate: function(date, which){
+ if (!which || which == 'date')
+ this.date = date;
+ if (!which || which == 'view')
+ this.viewDate = date;
+ this.fill();
+ this.setValue();
+ this.element.trigger({
+ type: 'changeDate',
+ date: this.date
+ });
+ var element;
+ if (this.isInput) {
+ element = this.element;
+ } else if (this.component){
+ element = this.element.find('input');
+ }
+ if (element) {
+ element.change();
+ if (this.autoclose) {
+ this.hide();
+ }
+ }
+ },
+
+ moveMonth: function(date, dir){
+ if (!dir) return date;
+ var new_date = new Date(date.valueOf()),
+ day = new_date.getUTCDate(),
+ month = new_date.getUTCMonth(),
+ mag = Math.abs(dir),
+ new_month, test;
+ dir = dir > 0 ? 1 : -1;
+ if (mag == 1){
+ test = dir == -1
+ // If going back one month, make sure month is not current month
+ // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
+ ? function(){ return new_date.getUTCMonth() == month; }
+ // If going forward one month, make sure month is as expected
+ // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
+ : function(){ return new_date.getUTCMonth() != new_month; };
+ new_month = month + dir;
+ new_date.setUTCMonth(new_month);
+ // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
+ if (new_month < 0 || new_month > 11)
+ new_month = (new_month + 12) % 12;
+ } else {
+ // For magnitudes >1, move one month at a time...
+ for (var i=0; i= this.startDate && date <= this.endDate;
+ },
+
+ keydown: function(e){
+ if (this.picker.is(':not(:visible)')){
+ if (e.keyCode == 27) // allow escape to hide and re-show picker
+ this.show();
+ return;
+ }
+ var dateChanged = false,
+ dir, day, month,
+ newDate, newViewDate;
+ switch(e.keyCode){
+ case 27: // escape
+ this.hide();
+ e.preventDefault();
+ break;
+ case 37: // left
+ case 39: // right
+ if (!this.keyboardNavigation) break;
+ dir = e.keyCode == 37 ? -1 : 1;
+ if (e.ctrlKey){
+ newDate = this.moveYear(this.date, dir);
+ newViewDate = this.moveYear(this.viewDate, dir);
+ } else if (e.shiftKey){
+ newDate = this.moveMonth(this.date, dir);
+ newViewDate = this.moveMonth(this.viewDate, dir);
+ } else {
+ newDate = new Date(this.date);
+ newDate.setUTCDate(this.date.getUTCDate() + dir);
+ newViewDate = new Date(this.viewDate);
+ newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
+ }
+ if (this.dateWithinRange(newDate)){
+ this.date = newDate;
+ this.viewDate = newViewDate;
+ this.setValue();
+ this.update();
+ e.preventDefault();
+ dateChanged = true;
+ }
+ break;
+ case 38: // up
+ case 40: // down
+ if (!this.keyboardNavigation) break;
+ dir = e.keyCode == 38 ? -1 : 1;
+ if (e.ctrlKey){
+ newDate = this.moveYear(this.date, dir);
+ newViewDate = this.moveYear(this.viewDate, dir);
+ } else if (e.shiftKey){
+ newDate = this.moveMonth(this.date, dir);
+ newViewDate = this.moveMonth(this.viewDate, dir);
+ } else {
+ newDate = new Date(this.date);
+ newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
+ newViewDate = new Date(this.viewDate);
+ newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
+ }
+ if (this.dateWithinRange(newDate)){
+ this.date = newDate;
+ this.viewDate = newViewDate;
+ this.setValue();
+ this.update();
+ e.preventDefault();
+ dateChanged = true;
+ }
+ break;
+ case 13: // enter
+ this.hide();
+ e.preventDefault();
+ break;
+ case 9: // tab
+ this.hide();
+ break;
+ }
+ if (dateChanged){
+ this.element.trigger({
+ type: 'changeDate',
+ date: this.date
+ });
+ var element;
+ if (this.isInput) {
+ element = this.element;
+ } else if (this.component){
+ element = this.element.find('input');
+ }
+ if (element) {
+ element.change();
+ }
+ }
+ },
+
+ showMode: function(dir) {
+ if (dir) {
+ this.viewMode = Math.max(0, Math.min(2, this.viewMode + dir));
+ }
+ this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).show();
+ this.updateNavArrows();
+ }
};
$.fn.datepicker = function ( option ) {
+ var args = Array.apply(null, arguments);
+ args.shift();
return this.each(function () {
var $this = $(this),
data = $this.data('datepicker'),
@@ -253,117 +642,195 @@
if (!data) {
$this.data('datepicker', (data = new Datepicker(this, $.extend({}, $.fn.datepicker.defaults,options))));
}
- if (typeof option == 'string') data[option]();
+ if (typeof option == 'string' && typeof data[option] == 'function') {
+ data[option].apply(data, args);
+ }
});
};
$.fn.datepicker.defaults = {
};
$.fn.datepicker.Constructor = Datepicker;
+ var dates = $.fn.datepicker.dates = {
+ en: {
+ days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
+ daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
+ daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
+ months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
+ today: "Today"
+ }
+ }
var DPGlobal = {
+ modes: [
+ {
+ clsName: 'days',
+ navFnc: 'Month',
+ navStep: 1
+ },
+ {
+ clsName: 'months',
+ navFnc: 'FullYear',
+ navStep: 1
+ },
+ {
+ clsName: 'years',
+ navFnc: 'FullYear',
+ navStep: 10
+ }],
isLeapYear: function (year) {
return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0))
},
getDaysInMonth: function (year, month) {
return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month]
},
- parseDate: function(dateStr, format) { //convert str into date
- dateStr = dateStr.replace(/:/g, '/');
- dateStr = dateStr.replace(/ /g, '/');
- strParts = dateStr.split('/');
-
- format = format.replace(/:/g, '/');
- format = format.replace(/ /g, '/');
- formatParts = format.split('/');
-
- date = new Date(),
- date.setHours(0); date.setMinutes(0); date.setSeconds(0); date.setMilliseconds(0);
-
- for (var key in formatParts) {
- if (typeof strParts[key] != 'undefined') {
- val = strParts[key];
- switch(formatParts[key]) {
- case 'dd':
+ validParts: /dd?|mm?|MM?|yy(?:yy)?/g,
+ nonpunctuation: /[^ -\/:-@\[-`{-~\t\n\r]+/g,
+ parseFormat: function(format){
+ // IE treats \0 as a string end in inputs (truncating the value),
+ // so it's a bad format delimiter, anyway
+ var separators = format.replace(this.validParts, '\0').split('\0'),
+ parts = format.match(this.validParts);
+ if (!separators || !separators.length || !parts || parts.length == 0){
+ throw new Error("Invalid date format.");
+ }
+ return {separators: separators, parts: parts};
+ },
+ parseDate: function(date, format, language) {
+ if (date instanceof Date) return date;
+ if (/^[-+]\d+[dmwy]([\s,]+[-+]\d+[dmwy])*$/.test(date)) {
+ var part_re = /([-+]\d+)([dmwy])/,
+ parts = date.match(/([-+]\d+)([dmwy])/g),
+ part, dir;
+ date = new Date();
+ for (var i=0; i'+
''+
- '← | '+
- ' | '+
- '→ | '+
+ ' | '+
+ ' | '+
+ ' | '+
'
'+
'',
- contTemplate: ' |
'
+ contTemplate: ' |
',
+ footTemplate: ' |
'
};
- DPGlobal.template = '
diff --git a/js/app.js b/js/app.js
index 48171eb9fbc..90bf28a8ac6 100644
--- a/js/app.js
+++ b/js/app.js
@@ -9,6 +9,7 @@ var modules = [
'kibana.services',
'kibana.directives',
'elasticjs.service',
+ '$strap.directives',
'kibana.panels',
]
@@ -17,7 +18,10 @@ 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/underscore.min.js")
+ .script("common/lib/bootstrap.min.js")
+ .script('common/lib/datepicker.js')
+ .script('common/lib/timepicker.js')
.script("common/lib/angular.min.js")
.script("common/lib/angular-strap.min.js")
.script("common/lib/elastic.min.js")
@@ -26,12 +30,12 @@ var labjs = $LAB
.script("common/lib/date.js")
.script("common/lib/datepicker.js")
.script("common/lib/shared.js")
+ .script("dashboards.js")
.script("js/services.js")
.script("js/controllers.js")
.script("js/filters.js")
.script("js/directives.js")
.script("js/panels.js")
- .script("dashboards.js");
_.each(config.modules, function(v) {
labjs = labjs.script('panels/'+v+'/module.js').wait()
@@ -49,5 +53,8 @@ labjs.wait(function(){
redirectTo: '/dashboard'
});
}]);
-
+ angular.element(document).ready(function() {
+ $('body').attr('ng-controller', 'DashCtrl')
+ angular.bootstrap(document, ['kibana']);
+ });
});
diff --git a/js/controllers.js b/js/controllers.js
index 2605feb1fe4..8c2ac37d45b 100644
--- a/js/controllers.js
+++ b/js/controllers.js
@@ -9,17 +9,20 @@ angular.module('kibana.controllers', [])
$scope.config = config;
$scope.dashboards = dashboards
$scope.timespan = config.timespan
- $scope.from = time_ago($scope.timespan);
- $scope.to = new Date();
-
- $scope.time_options = ['5m','15m','1h','6h','12h','24h','2d','7d','30d'];
+ $scope.time = {
+ from : time_ago($scope.timespan),
+ to : new Date()
+ }
+ // I'm leaving in all this refresh stuff until I figure out how index
+ // list caching should work. Maybe it should be handled by each time panel?
+ // That would require dashboard to contain a time panel. Hmm.
$scope.counter = 0;
$scope.playing = true;
$scope.play = function(){
$scope.counter++;
- $scope.to = new Date();
- $scope.from = time_ago($scope.timespan);
+ $scope.time.to = new Date();
+ $scope.time.from = time_ago($scope.timespan);
$scope.$root.$eval()
mytimeout = $timeout($scope.play,config.refresh);
}
@@ -37,9 +40,9 @@ angular.module('kibana.controllers', [])
// If from/to to change, update index list
$scope.$watch(function() {
- return angular.toJson([$scope.from, $scope.to])
+ return angular.toJson([$scope.time.from, $scope.time.to])
}, function(){
- indices($scope.from,$scope.to).then(function (p) {
+ indices($scope.time.from,$scope.time.to).then(function (p) {
$scope.index = p.join();
});
});
@@ -54,7 +57,7 @@ angular.module('kibana.controllers', [])
$scope.set_timespan = function(timespan) {
$scope.timespan = timespan;
- $scope.from = time_ago($scope.timespan);
+ $scope.time.from = time_ago($scope.timespan);
}
// returns a promise containing an array of all indices matching the index
diff --git a/panels/histogram/module.js b/panels/histogram/module.js
index 540b2de6f29..0295eb33d70 100644
--- a/panels/histogram/module.js
+++ b/panels/histogram/module.js
@@ -1,5 +1,5 @@
angular.module('kibana.histogram', [])
-.controller('histogram', function($scope, $location) {
+.controller('histogram', function($scope, $rootScope) {
// Set and populate defaults
var _d = {
@@ -14,14 +14,11 @@ angular.module('kibana.histogram', [])
? _d[k] : $scope.panel[k];
});
- if (!(_.isUndefined($scope.panel.group))) {
- $scope.$on($scope.panel.group+"-query", function(event, query) {
- $scope.panel.query[0].query = query;
- $scope.get_data();
- });
- }
-
$scope.get_data = function() {
+ // Make sure we have everything for the request to complete
+ if(_.isUndefined($scope.panel.time))
+ return
+
var request = $scope.ejs.Request().indices($scope.index);
// Build the question part of the query
@@ -30,8 +27,8 @@ angular.module('kibana.histogram', [])
queries.push($scope.ejs.FilteredQuery(
ejs.QueryStringQuery(v.query || '*'),
ejs.RangeFilter(config.timefield)
- .from($scope.from)
- .to($scope.to)
+ .from($scope.panel.time.from)
+ .to($scope.panel.time.to)
.cache(false))
)
});
@@ -53,15 +50,14 @@ angular.module('kibana.histogram', [])
results.then(function(results) {
$scope.hits = results.hits.total;
// Null values at each end of the time range make sure we see entire range
-
$scope.data = [];
_.each(results.facets, function(v, k) {
var series = {};
- var data = [[$scope.from.getTime(), null]];
+ var data = [[$scope.panel.time.from.getTime(), null]];
_.each(v.entries, function(v, k) {
data.push([v['time'],v['count']])
});
- data.push([$scope.to.getTime(), null])
+ data.push([$scope.panel.time.to.getTime(), null])
series.data = {
label: $scope.panel.query[k].label,
data: data,
@@ -73,13 +69,22 @@ angular.module('kibana.histogram', [])
});
}
- $scope.$watch(function() {
- return angular.toJson([$scope.from, $scope.to])
- }, function(){
- $scope.panel.interval = secondsToHms(
- calculate_interval($scope.from,$scope.to,50,0)/1000),
- $scope.get_data();
- });
+ if (!(_.isUndefined($scope.panel.group))) {
+ $scope.$on($scope.panel.group+"-query", function(event, query) {
+ $scope.panel.query[0].query = query;
+ $scope.get_data();
+ });
+ $scope.$on($scope.panel.group+"-time", function(event, time) {
+ $scope.panel.time = time;
+ $scope.panel.interval = secondsToHms(
+ calculate_interval(time.from,time.to,50,0)/1000),
+ $scope.get_data();
+ });
+ }
+
+ // Now that we're all setup, request the time from our group
+ $rootScope.$broadcast($scope.panel.group+"-get_time")
+
})
.directive('histogram', function() {
diff --git a/panels/map/module.js b/panels/map/module.js
index 55e9b66fbd4..eee56cfa886 100644
--- a/panels/map/module.js
+++ b/panels/map/module.js
@@ -1,5 +1,5 @@
angular.module('kibana.map', [])
-.controller('map', function($scope, $location) {
+.controller('map', function($scope, $rootScope) {
// Set and populate defaults
var _d = {
@@ -14,15 +14,11 @@ angular.module('kibana.map', [])
? _d[k] : $scope.panel[k];
});
-
- if (!(_.isUndefined($scope.panel.group))) {
- $scope.$on($scope.panel.group+"-query", function(event, query) {
- $scope.panel.query = query;
- $scope.get_data();
- });
- }
-
$scope.get_data = function() {
+ // Make sure we have everything for the request to complete
+ if(_.isUndefined($scope.panel.time))
+ return
+
var request = $scope.ejs.Request().indices($scope.index);
// Then the insert into facet and make the request
@@ -35,8 +31,8 @@ angular.module('kibana.map', [])
ejs.FilteredQuery(
ejs.QueryStringQuery($scope.panel.query || '*'),
ejs.RangeFilter(config.timefield)
- .from($scope.from)
- .to($scope.to)
+ .from($scope.panel.time.from)
+ .to($scope.panel.time.to)
.cache(false)
)))).size(0)
.doSearch();
@@ -51,11 +47,19 @@ angular.module('kibana.map', [])
});
}
- $scope.$watch(function() {
- return angular.toJson([$scope.from, $scope.to])
- }, function(){
- $scope.get_data();
- });
+ if (!(_.isUndefined($scope.panel.group))) {
+ $scope.$on($scope.panel.group+"-query", function(event, query) {
+ $scope.panel.query = query;
+ $scope.get_data();
+ });
+ $scope.$on($scope.panel.group+"-time", function(event, time) {
+ $scope.panel.time = time;
+ $scope.get_data();
+ });
+ }
+
+ // Now that we're all setup, request the time from our group
+ $rootScope.$broadcast($scope.panel.group+"-get_time")
})
.directive('map', function() {
diff --git a/panels/pie/module.js b/panels/pie/module.js
index a054ffcf7ea..672372cc495 100644
--- a/panels/pie/module.js
+++ b/panels/pie/module.js
@@ -2,7 +2,7 @@ labjs = labjs.script("common/lib/panels/jquery.flot.js")
.script("common/lib/panels/jquery.flot.pie.js")
angular.module('kibana.pie', [])
-.controller('pie', function($scope, $location) {
+.controller('pie', function($scope, $rootScope) {
// Set and populate defaults
var _d = {
@@ -26,6 +26,9 @@ angular.module('kibana.pie', [])
}
$scope.get_data = function() {
+ if(_.isUndefined($scope.panel.time))
+ return
+
var request = $scope.ejs.Request().indices($scope.index);
// If we have an array, use query facet
@@ -36,8 +39,8 @@ angular.module('kibana.pie', [])
queries.push(ejs.FilteredQuery(
ejs.QueryStringQuery(v.query || '*'),
ejs.RangeFilter(config.timefield)
- .from($scope.from)
- .to($scope.to)
+ .from($scope.panel.time.from)
+ .to($scope.panel.time.to)
.cache(false))
)
});
@@ -74,8 +77,8 @@ angular.module('kibana.pie', [])
ejs.FilteredQuery(
ejs.QueryStringQuery($scope.panel.query.query || '*'),
ejs.RangeFilter(config.timefield)
- .from($scope.from)
- .to($scope.to)
+ .from($scope.panel.time.from)
+ .to($scope.panel.time.to)
.cache(false)
)))).size(0)
.doSearch();
@@ -100,11 +103,19 @@ angular.module('kibana.pie', [])
}
}
- $scope.$watch(function() {
- return angular.toJson([$scope.from, $scope.to])
- }, function(){
- $scope.get_data();
- });
+ if (!(_.isUndefined($scope.panel.group))) {
+ $scope.$on($scope.panel.group+"-query", function(event, query) {
+ $scope.panel.query.query = query;
+ $scope.get_data();
+ });
+ $scope.$on($scope.panel.group+"-time", function(event, time) {
+ $scope.panel.time = time;
+ $scope.get_data();
+ });
+ }
+
+ // Now that we're all setup, request the time from our group
+ $rootScope.$broadcast($scope.panel.group+"-get_time")
})
.directive('pie', function() {
diff --git a/panels/sort/module.html b/panels/sort/module.html
index 3fc93f1a13d..44b9d8667cf 100644
--- a/panels/sort/module.html
+++ b/panels/sort/module.html
@@ -1,5 +1,6 @@
{{panel.title}}
+
+
-
\ No newline at end of file
diff --git a/panels/sort/module.js b/panels/sort/module.js
index 871728b3375..eb728932239 100644
--- a/panels/sort/module.js
+++ b/panels/sort/module.js
@@ -3,6 +3,7 @@ angular.module('kibana.sort', [])
// Set and populate defaults
var _d = {
+ label : "Sort",
query : "*",
size : 100,
sort : [config.timefield,'desc'],
diff --git a/panels/stringquery/module.html b/panels/stringquery/module.html
index 3a119b2eac2..d273a1659e2 100644
--- a/panels/stringquery/module.html
+++ b/panels/stringquery/module.html
@@ -1,7 +1,8 @@
{{panel.title}}
-
\ No newline at end of file
diff --git a/panels/stringquery/module.js b/panels/stringquery/module.js
index 804d34c01df..690b5bb796f 100644
--- a/panels/stringquery/module.js
+++ b/panels/stringquery/module.js
@@ -3,6 +3,7 @@ angular.module('kibana.stringquery', [])
// Set and populate defaults
var _d = {
+ label : "Search",
query : "*",
size : 100,
sort : [config.timefield,'desc'],
diff --git a/panels/table/module.js b/panels/table/module.js
index 9fd6a982fcf..09b70ab02d2 100644
--- a/panels/table/module.js
+++ b/panels/table/module.js
@@ -12,28 +12,23 @@ angular.module('kibana.table', [])
? _d[k] : $scope.panel[k];
});
- // Events which this panel receives and sends
- if (!(_.isUndefined($scope.panel.group))) {
- // Receives these events
- $scope.$on($scope.panel.group+"-query", function(event, query) {
- $scope.panel.query = query;
- $scope.get_data();
- });
- }
-
$scope.toggle_sort = function() {
$scope.panel.sort[1] = $scope.panel.sort[1] == 'asc' ? 'desc' : 'asc';
}
$scope.get_data = function() {
+ // Make sure we have everything for the request to complete
+ if(_.isUndefined($scope.panel.time))
+ return
+
var request = $scope.ejs.Request().indices($scope.index);
var results = request
.query(ejs.FilteredQuery(
ejs.QueryStringQuery($scope.panel.query || '*'),
ejs.RangeFilter(config.timefield)
- .from($scope.from)
- .to($scope.to)
+ .from($scope.panel.time.from)
+ .to($scope.panel.time.to)
.cache(false)
)
)
@@ -63,10 +58,22 @@ angular.module('kibana.table', [])
});
}
- $scope.$watch(function() {
- return angular.toJson([$scope.from, $scope.to, $scope.panel.sort])
- }, function(){
- $scope.get_data();
- });
+ $scope.$watch(function() {
+ return angular.toJson($scope.panel.sort)
+ }, function(){$scope.get_data()});
+
+ if (!(_.isUndefined($scope.panel.group))) {
+ $scope.$on($scope.panel.group+"-query", function(event, query) {
+ $scope.panel.query = query;
+ $scope.get_data();
+ });
+ $scope.$on($scope.panel.group+"-time", function(event, time) {
+ $scope.panel.time = time;
+ $scope.get_data();
+ });
+ }
+
+ // Now that we're all setup, request the time from our group
+ $rootScope.$broadcast($scope.panel.group+"-get_time")
})
diff --git a/panels/timepicker/module.html b/panels/timepicker/module.html
new file mode 100644
index 00000000000..8d66b2a791f
--- /dev/null
+++ b/panels/timepicker/module.html
@@ -0,0 +1,64 @@
+
\ No newline at end of file
diff --git a/panels/timepicker/module.js b/panels/timepicker/module.js
new file mode 100644
index 00000000000..072e3248600
--- /dev/null
+++ b/panels/timepicker/module.js
@@ -0,0 +1,168 @@
+angular.module('kibana.timepicker', [])
+.controller('timepicker', function($scope, $rootScope, $timeout) {
+
+ // Set and populate defaults
+ var _d = {
+ mode : "relative",
+ time_options : ['5m','15m','1h','6h','12h','24h','2d','7d','30d'],
+ timespan : '15m',
+ refresh : {
+ enable: false,
+ interval: 3,
+ min: 3
+ },
+ time : {
+ from : $scope.time.from,
+ to : $scope.time.to
+ }
+ }
+ _.each(_d, function(v, k) {
+ $scope.panel[k] = _.isUndefined($scope.panel[k])
+ ? _d[k] : $scope.panel[k];
+ });
+
+ // Private refresh interval that we can use for view display without causing
+ // unnecessary refreshes during changes
+ $scope.refresh_interval = $scope.panel.refresh.interval
+
+ // Init a private time object with Date() objects depending on mode
+ switch($scope.panel.mode) {
+ case 'absolute':
+ $scope.time = {
+ from : Date.parse($scope.panel.time.from),
+ to : Date.parse($scope.panel.time.to)
+ }
+ break;
+ case 'since':
+ $scope.time = {
+ from : Date.parse($scope.panel.time.from),
+ to : new Date()
+ }
+ break;
+ case 'relative':
+ $scope.time = {
+ from : time_ago($scope.panel.timespan),
+ to : new Date()
+ }
+ break;
+ }
+
+ // Init the values for the time/date pickers
+ $scope.timepicker = {
+ from : {
+ time : $scope.time.from.format("HH:MM:ss"),
+ date : $scope.time.from.format("mm/dd/yyyy")
+ },
+ to : {
+ time : $scope.time.to.format("HH:MM:ss"),
+ date : $scope.time.to.format("mm/dd/yyyy")
+ }
+ }
+
+ // 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' even to its group
+ if (!(_.isUndefined($scope.panel.group))) {
+ // Broadcast time when initializing
+ $rootScope.$broadcast($scope.panel.group+"-time", $scope.time)
+
+ // And whenever it is requested
+ $scope.$on($scope.panel.group+"-get_time", function(event) {
+ $rootScope.$broadcast($scope.panel.group+"-time", $scope.time)
+ });
+ }
+
+ $scope.$watch('panel.refresh.enable', function() {$scope.refresh()});
+ $scope.$watch('panel.refresh.interval', function() {
+ $timeout(function(){
+ if(_.isNumber($scope.panel.refresh.interval)) {
+ if($scope.panel.refresh.interval < $scope.panel.refresh.min) {
+ $scope.panel.refresh.interval = $scope.panel.refresh.min
+ $timeout.cancel($scope.panel.refresh.timer)
+ return;
+ }
+ $timeout.cancel($scope.panel.refresh.timer)
+ $scope.refresh()
+ } else {
+ $timeout.cancel($scope.panel.refresh.timer)
+ }
+ });
+ });
+
+
+ $scope.refresh = function() {
+ if ($scope.panel.refresh.enable) {
+ $scope.time_apply();
+ $scope.panel.refresh.timer = $timeout(
+ $scope.refresh,
+ $scope.panel.refresh.interval*1000
+ );
+ } else {
+ $timeout.cancel($scope.panel.refresh.timer)
+ }
+ }
+
+ $scope.set_mode = function(mode) {
+ $scope.panel.mode = mode;
+ $scope.panel.refresh.enable = mode === 'absolute' ?
+ false : $scope.panel.refresh.enable
+ }
+
+ $scope.to_now = function() {
+ $scope.timepicker.to = {
+ time : new Date().format("HH:MM:ss"),
+ date : new Date().format("mm/dd/yyyy")
+ }
+ }
+
+ $scope.set_timespan = function(timespan) {
+ $scope.panel.timespan = timespan;
+ $scope.timepicker.from = {
+ time : time_ago(timespan).format("HH:MM:ss"),
+ date : time_ago(timespan).format("mm/dd/yyyy")
+ }
+ $scope.time_apply();
+ }
+
+ $scope.time_check = function(){
+ var from = $scope.panel.mode === 'relative' ? time_ago($scope.panel.timespan) :
+ Date.parse($scope.timepicker.from.date + " " + $scope.timepicker.from.time)
+ var to = $scope.panel.mode !== 'absolute' ? new Date() :
+ Date.parse($scope.timepicker.to.date + " " + $scope.timepicker.to.time)
+
+ if (from.getTime() >= to.getTime())
+ from = new Date(to.getTime() - 1000)
+
+ // Janky 0s timeout to get around $scope queue processing view issue
+ $timeout(function(){
+ $scope.timepicker = {
+ from : {
+ time : from.format("HH:MM:ss"),
+ date : from.format("mm/dd/yyyy")
+ },
+ to : {
+ time : to.format("HH:MM:ss"),
+ date : to.format("mm/dd/yyyy")
+ }
+ }
+ });
+ }
+
+ $scope.time_apply = function() {
+ $scope.time_check();
+ // Update internal time object
+ $scope.time = {
+ from : Date.parse($scope.timepicker.from.date + " " + $scope.timepicker.from.time),
+ to : Date.parse($scope.timepicker.to.date + " " + $scope.timepicker.to.time)
+ };
+
+ // Broadcast time
+ $rootScope.$broadcast($scope.panel.group+"-time", $scope.time)
+
+ // Update panel's string representation of the time object
+ $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")
+ };
+ };
+
+})
\ No newline at end of file
diff --git a/panels/timepicker/refreshctrl.html b/panels/timepicker/refreshctrl.html
new file mode 100644
index 00000000000..07d2a8603b6
--- /dev/null
+++ b/panels/timepicker/refreshctrl.html
@@ -0,0 +1,5 @@
+
\ No newline at end of file