mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
feat(timepickerv2): big progress on new design of new timepicker, #2761
This commit is contained in:
parent
a30f73fe36
commit
5eefa36111
@ -56,7 +56,8 @@ function (angular, $, _, appLevelRequire) {
|
|||||||
'ang-drag-drop',
|
'ang-drag-drop',
|
||||||
'grafana',
|
'grafana',
|
||||||
'pasvaz.bindonce',
|
'pasvaz.bindonce',
|
||||||
'ui.bootstrap.tabs',
|
'ui.bootstrap',
|
||||||
|
'ui.bootstrap.tpls',
|
||||||
];
|
];
|
||||||
|
|
||||||
var module_types = ['controllers', 'directives', 'factories', 'services', 'filters', 'routes'];
|
var module_types = ['controllers', 'directives', 'factories', 'services', 'filters', 'routes'];
|
||||||
|
@ -19,7 +19,7 @@ require.config({
|
|||||||
'angular-sanitize': 'vendor/angular-sanitize/angular-sanitize',
|
'angular-sanitize': 'vendor/angular-sanitize/angular-sanitize',
|
||||||
'angular-dragdrop': 'vendor/angular-native-dragdrop/draganddrop',
|
'angular-dragdrop': 'vendor/angular-native-dragdrop/draganddrop',
|
||||||
'angular-strap': 'vendor/angular-other/angular-strap',
|
'angular-strap': 'vendor/angular-other/angular-strap',
|
||||||
'angular-ui': 'vendor/angular-ui/tabs',
|
'angular-ui': 'vendor/angular-ui/ui-bootstrap-tpls',
|
||||||
timepicker: 'vendor/angular-other/timepicker',
|
timepicker: 'vendor/angular-other/timepicker',
|
||||||
datepicker: 'vendor/angular-other/datepicker',
|
datepicker: 'vendor/angular-other/datepicker',
|
||||||
bindonce: 'vendor/angular-bindonce/bindonce',
|
bindonce: 'vendor/angular-bindonce/bindonce',
|
||||||
|
@ -72,7 +72,8 @@ function ($, coreModule) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var src = "'" + payload.src + "'";
|
var src = "'" + payload.src + "'";
|
||||||
var view = $('<div class="gf-box" ng-include="' + src + '"></div>');
|
var cssClass = payload.cssClass || 'gf-box';
|
||||||
|
var view = $('<div class="' + cssClass + '" ng-include="' + src + '"></div>');
|
||||||
|
|
||||||
if (payload.cssClass) {
|
if (payload.cssClass) {
|
||||||
view.addClass(payload.cssClass);
|
view.addClass(payload.cssClass);
|
||||||
|
@ -15,12 +15,12 @@ var spans = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var rangeOptions = [
|
var rangeOptions = [
|
||||||
{ from: 'now/d', to: 'now/d', display: 'Today', section: 0 },
|
{ from: 'now/d', to: 'now/d', display: 'Today', section: 2 },
|
||||||
{ from: 'now/w', to: 'now/w', display: 'This week', section: 0 },
|
{ from: 'now/w', to: 'now/w', display: 'This week', section: 2 },
|
||||||
{ from: 'now/d', to: 'now', display: 'The day so far', section: 0 },
|
{ from: 'now/d', to: 'now', display: 'The day so far', section: 2 },
|
||||||
{ from: 'now/w', to: 'now', display: 'Week to date', section: 0 },
|
{ from: 'now/w', to: 'now', display: 'Week to date', section: 2 },
|
||||||
{ from: 'now/M', to: 'now/M', display: 'This month', section: 0 },
|
{ from: 'now/M', to: 'now/M', display: 'This month', section: 2 },
|
||||||
{ from: 'now/y', to: 'now/y', display: 'This year', section: 0 },
|
{ from: 'now/y', to: 'now/y', display: 'This year', section: 2 },
|
||||||
|
|
||||||
{ from: 'now-1d/d', to: 'now-1d/d', display: 'Yesterday', section: 1 },
|
{ from: 'now-1d/d', to: 'now-1d/d', display: 'Yesterday', section: 1 },
|
||||||
{ from: 'now-2d/d', to: 'now-2d/d', display: 'Day before yesterday', section: 1 },
|
{ from: 'now-2d/d', to: 'now-2d/d', display: 'Day before yesterday', section: 1 },
|
||||||
@ -29,22 +29,22 @@ var rangeOptions = [
|
|||||||
{ from: 'now-1M/M', to: 'now-1M/M', display: 'Previous month', section: 1 },
|
{ from: 'now-1M/M', to: 'now-1M/M', display: 'Previous month', section: 1 },
|
||||||
{ from: 'now-1y/y', to: 'now-1y/y', display: 'Previous year', section: 1 },
|
{ from: 'now-1y/y', to: 'now-1y/y', display: 'Previous year', section: 1 },
|
||||||
|
|
||||||
{ from: 'now-5m', to: 'now', display: 'Last 5 minutes', section: 2 },
|
{ from: 'now-5m', to: 'now', display: 'Last 5 minutes', section: 3 },
|
||||||
{ from: 'now-15m', to: 'now', display: 'Last 15 minutes', section: 2 },
|
{ from: 'now-15m', to: 'now', display: 'Last 15 minutes', section: 3 },
|
||||||
{ from: 'now-30m', to: 'now', display: 'Last 30 minutes', section: 2 },
|
{ from: 'now-30m', to: 'now', display: 'Last 30 minutes', section: 3 },
|
||||||
{ from: 'now-1h', to: 'now', display: 'Last 1 hour', section: 2 },
|
{ from: 'now-1h', to: 'now', display: 'Last 1 hour', section: 3 },
|
||||||
{ from: 'now-4h', to: 'now', display: 'Last 4 hours', section: 2 },
|
{ from: 'now-6h', to: 'now', display: 'Last 6 hours', section: 3 },
|
||||||
{ from: 'now-12h', to: 'now', display: 'Last 12 hours', section: 2 },
|
{ from: 'now-12h', to: 'now', display: 'Last 12 hours', section: 3 },
|
||||||
{ from: 'now-24h', to: 'now', display: 'Last 24 hours', section: 2 },
|
{ from: 'now-24h', to: 'now', display: 'Last 24 hours', section: 3 },
|
||||||
{ from: 'now-7d', to: 'now', display: 'Last 7 days', section: 2 },
|
{ from: 'now-7d', to: 'now', display: 'Last 7 days', section: 3 },
|
||||||
|
|
||||||
{ from: 'now-30d', to: 'now', display: 'Last 30 days', section: 3 },
|
{ from: 'now-30d', to: 'now', display: 'Last 30 days', section: 0 },
|
||||||
{ from: 'now-60d', to: 'now', display: 'Last 60 days', section: 3 },
|
{ from: 'now-60d', to: 'now', display: 'Last 60 days', section: 0 },
|
||||||
{ from: 'now-90d', to: 'now', display: 'Last 90 days', section: 3 },
|
{ from: 'now-90d', to: 'now', display: 'Last 90 days', section: 0 },
|
||||||
{ from: 'now-6M', to: 'now', display: 'Last 6 months', section: 3 },
|
{ from: 'now-6M', to: 'now', display: 'Last 6 months', section: 0 },
|
||||||
{ from: 'now-1y', to: 'now', display: 'Last 1 year', section: 3 },
|
{ from: 'now-1y', to: 'now', display: 'Last 1 year', section: 0 },
|
||||||
{ from: 'now-2y', to: 'now', display: 'Last 2 years', section: 3 },
|
{ from: 'now-2y', to: 'now', display: 'Last 2 years', section: 0 },
|
||||||
{ from: 'now-5y', to: 'now', display: 'Last 5 years', section: 3 },
|
{ from: 'now-5y', to: 'now', display: 'Last 5 years', section: 0 },
|
||||||
];
|
];
|
||||||
|
|
||||||
var rangeIndex = {};
|
var rangeIndex = {};
|
||||||
@ -52,10 +52,14 @@ _.each(rangeOptions, function (frame) {
|
|||||||
rangeIndex[frame.from + ' to ' + frame.to] = frame;
|
rangeIndex[frame.from + ' to ' + frame.to] = frame;
|
||||||
});
|
});
|
||||||
|
|
||||||
function getRelativeTimesList(timepickerSettings) {
|
function getRelativeTimesList(timepickerSettings, currentDisplay) {
|
||||||
return _.map(timepickerSettings.time_options, function(duration: string) {
|
return _.groupBy(rangeOptions, (option: any) => {
|
||||||
return describeTextRange(duration);
|
option.active = option.display === currentDisplay;
|
||||||
|
return option.section;
|
||||||
});
|
});
|
||||||
|
// return _.map(timepickerSettings.time_options, function(duration: string) {
|
||||||
|
// return describeTextRange(duration);
|
||||||
|
// });
|
||||||
}
|
}
|
||||||
|
|
||||||
// handles expressions like
|
// handles expressions like
|
||||||
|
@ -216,6 +216,10 @@ function (angular, $, kbn, _, moment) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
p.formatDate = function(date, format) {
|
p.formatDate = function(date, format) {
|
||||||
|
if (!moment.isMoment(date)) {
|
||||||
|
date = moment(date)
|
||||||
|
}
|
||||||
|
|
||||||
format = format || 'YYYY-MM-DD HH:mm:ss';
|
format = format || 'YYYY-MM-DD HH:mm:ss';
|
||||||
|
|
||||||
return this.timezone === 'browser' ?
|
return this.timezone === 'browser' ?
|
||||||
|
@ -9,40 +9,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="gf-box-body">
|
<div class="gf-box-body">
|
||||||
<style>
|
|
||||||
.timepicker-to-column {
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timepicker-input input {
|
|
||||||
outline: 0 !important;
|
|
||||||
border: 0px !important;
|
|
||||||
-webkit-box-shadow: 0;
|
|
||||||
-moz-box-shadow: 0;
|
|
||||||
box-shadow: 0;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timepicker-input input::-webkit-outer-spin-button,
|
|
||||||
.timepicker-input input::-webkit-inner-spin-button {
|
|
||||||
-webkit-appearance: none;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
input.timepicker-date {
|
|
||||||
width: 90px;
|
|
||||||
}
|
|
||||||
input.timepicker-hms {
|
|
||||||
width: 20px;
|
|
||||||
}
|
|
||||||
input.timepicker-ms {
|
|
||||||
width: 25px;
|
|
||||||
}
|
|
||||||
div.timepicker-now {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="timepicker form-horizontal">
|
<div class="timepicker form-horizontal">
|
||||||
<form name="timeForm" style="margin-bottom: 0">
|
<form name="timeForm" style="margin-bottom: 0">
|
||||||
|
|
||||||
|
90
public/app/features/dashboard/timepicker/dropdown.html
Normal file
90
public/app/features/dashboard/timepicker/dropdown.html
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
<div class="row pull-right">
|
||||||
|
<div class="gf-timepicker-relative-section">
|
||||||
|
<h3>Quick ranges</h3>
|
||||||
|
<ul ng-repeat="group in timeOptions">
|
||||||
|
<li bindonce ng-repeat='option in group' ng-class="{active: option.active}">
|
||||||
|
<a ng-click="ctrl.setRelativeFilter(option)" bo-text="option.display"></a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="gf-timepicker-absolute-section">
|
||||||
|
<h3>Time range</h3>
|
||||||
|
<label class="small">From:</label>
|
||||||
|
<div class="input-prepend">
|
||||||
|
<input type="text" class="input-large" ng-model="timeRaw.from" input-datetime>
|
||||||
|
<button class="btn btn-primary" type="button" ng-click="openFromPicker=!openFromPicker">
|
||||||
|
<i class="fa fa-calendar"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div ng-if="openFromPicker">
|
||||||
|
<datepicker ng-model="absolute.to" class="gf-timepicker-component" show-weeks="false"></datepicker>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label class="small">To:</label>
|
||||||
|
<div class="input-prepend">
|
||||||
|
<input type="text" class="input-large" ng-model="timeRaw.to" input-datetime>
|
||||||
|
<button class="btn btn-primary" type="button" ng-click="openToPicker=!openToPicker">
|
||||||
|
<i class="fa fa-calendar"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label class="small">Refreshing every:</label>
|
||||||
|
<select ng-model="dashboard.refresh" class='input-large' ng-options="f for f in ['5m','10m']"></select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="clearfix"></div>
|
||||||
|
|
||||||
|
<!-- <tabset> -->
|
||||||
|
<!-- <tab heading="Relative"> -->
|
||||||
|
<!-- -->
|
||||||
|
<!-- <div style="float:right; width: 200px" ng-repeat="group in timeOptions"> -->
|
||||||
|
<!-- <ul> -->
|
||||||
|
<!-- <li bindonce ng-repeat='option in group'> -->
|
||||||
|
<!-- <a ng-click="ctrl.setRelativeFilter(option)" bo-text="option.display"></a> -->
|
||||||
|
<!-- </li> -->
|
||||||
|
<!-- </ul> -->
|
||||||
|
<!-- </div> -->
|
||||||
|
<!-- -->
|
||||||
|
<!-- </tab> -->
|
||||||
|
<!-- <tab heading="Absolute"> -->
|
||||||
|
<!-- -->
|
||||||
|
<!-- <form name="timeForm" style="margin: 0 20px 20px 20px"> -->
|
||||||
|
<!-- <div class="gf-timepicker-section"> -->
|
||||||
|
<!-- <div> -->
|
||||||
|
<!-- <div class="tight-form last"> -->
|
||||||
|
<!-- </div> -->
|
||||||
|
<!-- <label class="small">From:</label> -->
|
||||||
|
<!-- <input type="text" required class="input-large" ng-model="absolute.from" input-datetime="MMMM Do YYYY, HH:mm:ss.SSS"> -->
|
||||||
|
<!-- <br> -->
|
||||||
|
<!-- </div> -->
|
||||||
|
<!-- <datepicker ng-model="absolute.from" class="gf-timepicker-component" show-weeks="false"></datepicker> -->
|
||||||
|
<!-- </div> -->
|
||||||
|
<!-- <div class="gf-timepicker-section"> -->
|
||||||
|
<!-- <div> -->
|
||||||
|
<!-- <label class="small">To:</label> -->
|
||||||
|
<!-- <input type="text" required class="input-large" ng-model="absolute.to" input-datetime="MMMM Do YYYY, HH:mm:ss.SSS"> -->
|
||||||
|
<!-- <br> -->
|
||||||
|
<!-- </div> -->
|
||||||
|
<!-- <datepicker ng-model="absolute.to" class="gf-timepicker-component" show-weeks="false"></datepicker> -->
|
||||||
|
<!-- </div> -->
|
||||||
|
<!-- <div class="clearfix"></div> -->
|
||||||
|
<!-- </form> -->
|
||||||
|
<!-- </tab> -->
|
||||||
|
<!-- </tabset> -->
|
||||||
|
<!-- -->
|
||||||
|
<!-- <!-- <!&#45;&#45; Auto refresh submenu &#45;&#45;> --> -->
|
||||||
|
<!-- <li class="dropdown-submenu"> -->
|
||||||
|
<!-- <a href="#">Auto-Refresh</a> -->
|
||||||
|
<!-- <ul class="dropdown-menu" ng-class="{'dropdown-submenu-left': refreshMenuLeftSide}"> -->
|
||||||
|
<!-- <li> -->
|
||||||
|
<!-- <a ng-click="timeSrv.set_interval(false)">Off</a> -->
|
||||||
|
<!-- </li> -->
|
||||||
|
<!-- <li bindonce ng-repeat="interval in panel.refresh_intervals track by $index"> -->
|
||||||
|
<!-- <a ng-click="timeSrv.set_interval(interval)" bo-text="'Every ' + interval"></a> -->
|
||||||
|
<!-- </li> -->
|
||||||
|
<!-- </ul> -->
|
||||||
|
<!-- </li> -->
|
||||||
|
<!-- <li><a ng-click="ctrl.customTime()">Custom</a></li> -->
|
||||||
|
<!-- </div> -->
|
33
public/app/features/dashboard/timepicker/input_date.js
Normal file
33
public/app/features/dashboard/timepicker/input_date.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
define([
|
||||||
|
"angular",
|
||||||
|
"moment",
|
||||||
|
],function (angular, moment) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
angular.
|
||||||
|
module("grafana.directives").
|
||||||
|
directive('inputDatetime', function () {
|
||||||
|
return {
|
||||||
|
restrict: 'A',
|
||||||
|
require: 'ngModel',
|
||||||
|
link: function ($scope, $elem, attrs, ngModel) {
|
||||||
|
// var format = 'YYYY-MM-DD HH:mm:ss.SSS';
|
||||||
|
// // $elem.after('<div class="input-datetime-format">' + format + '</div>');
|
||||||
|
//
|
||||||
|
// // What should I make with the input from the user?
|
||||||
|
// var fromUser = function (text) {
|
||||||
|
// var parsed = moment(text, format);
|
||||||
|
// return parsed.isValid() ? parsed : undefined;
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// // How should I present the data back to the user in the input field?
|
||||||
|
// var toUser = function (datetime) {
|
||||||
|
// return moment(datetime).format(format);
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// ngModel.$parsers.push(fromUser);
|
||||||
|
// ngModel.$formatters.push(toUser);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
@ -1,56 +1,24 @@
|
|||||||
<style>
|
<ul class="nav timepicker-dropdown">
|
||||||
.timepicker-timestring {
|
|
||||||
font-weight: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timepicker-dropdown {
|
<li class="grafana-menu-zoom-out">
|
||||||
margin: 0px !important;
|
<a class='small' ng-click='zoom(2)'>
|
||||||
border: 0px !important;
|
Zoom Out
|
||||||
}
|
</a>
|
||||||
</style>
|
</li>
|
||||||
<form name="input" style="margin:0">
|
|
||||||
<ul class="nav timepicker-dropdown">
|
|
||||||
|
|
||||||
<li class="grafana-menu-zoom-out">
|
<li>
|
||||||
<a class='small' ng-click='zoom(2)'>
|
<a class="timepicker-dropdown" bs-tooltip="tooltip" data-placement="bottom" ng-click="ctrl.loadTimeOptions()">
|
||||||
Zoom Out
|
<i class="fa fa-clock-o"></i>
|
||||||
</a>
|
<span ng-bind="rangeString"></span>
|
||||||
</li>
|
<i class="fa fa-caret-down"></i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li class="dropdown">
|
<li class="grafana-menu-refresh">
|
||||||
|
<a ng-click="timeSrv.refreshDashboard()">
|
||||||
|
<i class="fa fa-refresh"></i>
|
||||||
|
<span ng-show="dashboard.refresh" class="text-warning">refreshed every {{dashboard.refresh}} </span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
<a class="dropdown-toggle timepicker-dropdown" data-toggle="dropdown" bs-tooltip="time.tooltip" data-placement="bottom" ng-click="ctrl.loadTimeOptions();">
|
</ul>
|
||||||
<i class="fa fa-clock-o"></i>
|
|
||||||
<span ng-bind="time.rangeString"></span>
|
|
||||||
<span ng-show="dashboard.refresh" class="text-warning">refreshed every {{dashboard.refresh}} </span>
|
|
||||||
<i class="fa fa-caret-down"></i>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<!-- lacy load this -->
|
|
||||||
<ul class="dropdown-menu" ng-if="timeOptions" >
|
|
||||||
<li bindonce ng-repeat='option in timeOptions'>
|
|
||||||
<a ng-click="ctrl.setRelativeFilter(option)" bo-text="option.display"></a>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<!-- Auto refresh submenu -->
|
|
||||||
<li class="dropdown-submenu">
|
|
||||||
<a href="#">Auto-Refresh</a>
|
|
||||||
<ul class="dropdown-menu" ng-class="{'dropdown-submenu-left': refreshMenuLeftSide}">
|
|
||||||
<li>
|
|
||||||
<a ng-click="timeSrv.set_interval(false)">Off</a>
|
|
||||||
</li>
|
|
||||||
<li bindonce ng-repeat="interval in panel.refresh_intervals track by $index">
|
|
||||||
<a ng-click="timeSrv.set_interval(interval)" bo-text="'Every ' + interval"></a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li><a ng-click="ctrl.customTime()">Custom</a></li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
</li>
|
|
||||||
<li ng-show="!dashboard.refresh" class="grafana-menu-refresh">
|
|
||||||
<a ng-click="timeSrv.refreshDashboard()"><i class="fa fa-refresh"></i></a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
///<reference path="../../../headers/common.d.ts" />
|
///<reference path="../../../headers/common.d.ts" />
|
||||||
|
///<amd-dependency path="./input_date" name="inputDate" />
|
||||||
|
|
||||||
import angular = require('angular');
|
import angular = require('angular');
|
||||||
import _ = require('lodash');
|
import _ = require('lodash');
|
||||||
@ -7,6 +8,8 @@ import kbn = require('kbn');
|
|||||||
import dateMath = require('app/core/utils/datemath');
|
import dateMath = require('app/core/utils/datemath');
|
||||||
import rangeUtil = require('app/core/utils/rangeutil');
|
import rangeUtil = require('app/core/utils/rangeutil');
|
||||||
|
|
||||||
|
declare var inputDate: any;
|
||||||
|
|
||||||
export class TimePickerCtrl {
|
export class TimePickerCtrl {
|
||||||
|
|
||||||
static defaults = {
|
static defaults = {
|
||||||
@ -44,57 +47,30 @@ export class TimePickerCtrl {
|
|||||||
|
|
||||||
if (_.isString(timeRaw.to) && timeRaw.to.indexOf('now') === 0) {
|
if (_.isString(timeRaw.to) && timeRaw.to.indexOf('now') === 0) {
|
||||||
this.$scope.panel.now = true;
|
this.$scope.panel.now = true;
|
||||||
|
this.$scope.rangeString = rangeUtil.describeTimeRange(timeRaw);
|
||||||
|
} else {
|
||||||
|
let format = 'MMM D, YYYY HH:mm:ss';
|
||||||
|
this.$scope.rangeString = this.$scope.dashboard.formatDate(time.from, format) + ' to ' +
|
||||||
|
this.$scope.dashboard.formatDate(time.to, format);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$scope.time = this.getScopeTimeObj(time.from, time.to);
|
this.$scope.timeRaw = timeRaw;
|
||||||
|
this.$scope.absolute = {form: time.from.toDate(), to: time.to.toDate()};
|
||||||
this.$scope.onAppEvent('zoom-out', function() {
|
this.$scope.onAppEvent('zoom-out', function() {
|
||||||
this.$scope.zoom(2);
|
this.$scope.zoom(2);
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
pad(n: number, width: number, z = 0): string {
|
this.$scope.tooltip = this.$scope.dashboard.formatDate(time.from.date) + ' <br>to<br>';
|
||||||
var str = n.toString();
|
this.$scope.tooltip += this.$scope.dashboard.formatDate(time.to.date);
|
||||||
return str.length >= width ? str : new Array(width - str.length + 1).join(z.toString()) + str;
|
|
||||||
}
|
|
||||||
|
|
||||||
getTimeObj(date): any {
|
|
||||||
return {
|
|
||||||
date: date,
|
|
||||||
hour: this.pad(date.hours(), 2),
|
|
||||||
minute: this.pad(date.minutes(), 2),
|
|
||||||
second: this.pad(date.seconds(), 2),
|
|
||||||
millisecond: this.pad(date.milliseconds(), 3)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
getScopeTimeObj(from, to) {
|
|
||||||
var model : any = {from: this.getTimeObj(from), to: this.getTimeObj(to)};
|
|
||||||
|
|
||||||
if (model.from.date) {
|
|
||||||
model.tooltip = this.$scope.dashboard.formatDate(model.from.date) + ' <br>to<br>';
|
|
||||||
model.tooltip += this.$scope.dashboard.formatDate(model.to.date);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
model.tooltip = 'Click to set time filter';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.timeSrv.time) {
|
|
||||||
if (this.$scope.panel.now) {
|
|
||||||
model.rangeString = rangeUtil.describeTimeRange(this.timeSrv.time);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
model.rangeString = this.$scope.dashboard.formatDate(model.from.date, 'MMM D, YYYY HH:mm:ss') + ' to ' +
|
|
||||||
this.$scope.dashboard.formatDate(model.to.date, 'MMM D, YYYY HH:mm:ss');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return model;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
loadTimeOptions() {
|
loadTimeOptions() {
|
||||||
this.$scope.timeOptions = rangeUtil.getRelativeTimesList(this.$scope.panel);
|
this.$scope.timeOptions = rangeUtil.getRelativeTimesList(this.$scope.panel, this.$scope.rangeString);
|
||||||
this.$scope.refreshMenuLeftSide = this.$scope.time.rangeString.length < 10;
|
this.$scope.appEvent('show-dash-editor', {
|
||||||
|
src: 'app/features/dashboard/timepicker/dropdown.html',
|
||||||
|
scope: this.$scope,
|
||||||
|
cssClass: 'gf-timepicker-dropdown',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
customTime() {
|
customTime() {
|
||||||
@ -118,10 +94,6 @@ export class TimePickerCtrl {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
setNow() {
|
|
||||||
this.$scope.time.to = this.getTimeObj(new Date());
|
|
||||||
}
|
|
||||||
|
|
||||||
setAbsoluteTimeFilter(time) {
|
setAbsoluteTimeFilter(time) {
|
||||||
// Create filter object
|
// Create filter object
|
||||||
var _filter = _.clone(time);
|
var _filter = _.clone(time);
|
||||||
@ -130,8 +102,6 @@ export class TimePickerCtrl {
|
|||||||
_filter.to = "now";
|
_filter.to = "now";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update our representation
|
|
||||||
this.$scope.time = this.getScopeTimeObj(time.from, time.to);
|
|
||||||
this.timeSrv.setTime(_filter);
|
this.timeSrv.setTime(_filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,8 +115,7 @@ export class TimePickerCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.timeSrv.setTime(range);
|
this.timeSrv.setTime(range);
|
||||||
|
this.$scope.appEvent('hide-dash-editor');
|
||||||
this.$scope.time = this.getScopeTimeObj(dateMath.parse(range.from), moment());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
validate(time): any {
|
validate(time): any {
|
||||||
|
@ -542,5 +542,3 @@ a.thumbnail {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MEDIA QUERIES
|
|
||||||
// -----------------------------------------------------
|
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
@import "validation.less";
|
@import "validation.less";
|
||||||
@import "fonts.less";
|
@import "fonts.less";
|
||||||
@import "tabs.less";
|
@import "tabs.less";
|
||||||
|
@import "timepicker.less";
|
||||||
|
|
||||||
.row-control-inner {
|
.row-control-inner {
|
||||||
padding:0px;
|
padding:0px;
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
.nav-tabs-alt {
|
.nav-tabs-alt {
|
||||||
border-bottom: @grafanaTriggerBorder;
|
border-bottom: @grafanaTriggerBorder;
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
& > li > a {
|
& > li > a {
|
||||||
color: darken(@linkColor, 20%);
|
color: darken(@linkColor, 20%);
|
||||||
@ -31,3 +32,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tab-content {
|
||||||
|
padding: 10px;
|
||||||
|
background-color: @grafanaPanelBackground;
|
||||||
|
}
|
||||||
|
|
||||||
|
104
public/less/timepicker.less
Normal file
104
public/less/timepicker.less
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
.timepicker-to-column {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timepicker-input input {
|
||||||
|
outline: 0 !important;
|
||||||
|
border: 0px !important;
|
||||||
|
-webkit-box-shadow: 0;
|
||||||
|
-moz-box-shadow: 0;
|
||||||
|
box-shadow: 0;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timepicker-input input::-webkit-outer-spin-button,
|
||||||
|
.timepicker-input input::-webkit-inner-spin-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.timepicker-date {
|
||||||
|
width: 90px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.timepicker-hms {
|
||||||
|
width: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.timepicker-ms {
|
||||||
|
width: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.timepicker-now {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timepicker-timestring {
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gf-timepicker-dropdown {
|
||||||
|
margin: 15px;
|
||||||
|
padding: 10px 20px;
|
||||||
|
float: right;
|
||||||
|
background-color: @grafanaPanelBackground;
|
||||||
|
border: 1px solid @grafanaTargetFuncBackground;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gf-timepicker-absolute-section {
|
||||||
|
width: 300px;
|
||||||
|
float: left;
|
||||||
|
padding: 0 20px 0 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gf-timepicker-relative-section {
|
||||||
|
border-right: @grafanaTriggerBorder;
|
||||||
|
padding: 0 20px 0 20px;
|
||||||
|
min-height: 258px;
|
||||||
|
float: left;
|
||||||
|
ul {
|
||||||
|
float: left;
|
||||||
|
margin: 0 20px 10px 25px;
|
||||||
|
li.active {
|
||||||
|
border-left: 1px solid @blue;
|
||||||
|
padding: 2px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.gf-timepicker-component {
|
||||||
|
button.btn-sm {
|
||||||
|
.buttonBackground(@btnInverseBackground, @btnInverseBackgroundHighlight);
|
||||||
|
background-image: none;
|
||||||
|
border: none;
|
||||||
|
padding: 6px 9px;
|
||||||
|
color: @textColor;
|
||||||
|
&.active .text-info {
|
||||||
|
color: @orange;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.faMixin {
|
||||||
|
display: inline-block;
|
||||||
|
font: normal normal normal 14px/1 FontAwesome;
|
||||||
|
font-size: inherit;
|
||||||
|
text-rendering: auto;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
.glyphicon-chevron-right:before {
|
||||||
|
.faMixin;
|
||||||
|
content: "\f054";
|
||||||
|
}
|
||||||
|
|
||||||
|
.glyphicon-chevron-left:before {
|
||||||
|
.faMixin;
|
||||||
|
content: "\f053";
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-datetime-format {
|
||||||
|
color: @linkColorDisabled
|
||||||
|
}
|
136
public/vendor/angular-other/angular-strap.js
vendored
136
public/vendor/angular-other/angular-strap.js
vendored
@ -14,142 +14,6 @@ angular.module('$strap', [
|
|||||||
'$strap.config'
|
'$strap.config'
|
||||||
]);
|
]);
|
||||||
'use strict';
|
'use strict';
|
||||||
angular.module('$strap.directives').directive('bsDatepicker', [
|
|
||||||
'$timeout',
|
|
||||||
'$strapConfig',
|
|
||||||
function ($timeout, $strapConfig) {
|
|
||||||
var isAppleTouch = /(iP(a|o)d|iPhone)/g.test(navigator.userAgent);
|
|
||||||
var regexpMap = function regexpMap(language) {
|
|
||||||
language = language || 'en';
|
|
||||||
return {
|
|
||||||
'/': '[\\/]',
|
|
||||||
'-': '[-]',
|
|
||||||
'.': '[.]',
|
|
||||||
' ': '[\\s]',
|
|
||||||
'dd': '(?:(?:[0-2]?[0-9]{1})|(?:[3][01]{1}))',
|
|
||||||
'd': '(?:(?:[0-2]?[0-9]{1})|(?:[3][01]{1}))',
|
|
||||||
'mm': '(?:[0]?[1-9]|[1][012])',
|
|
||||||
'm': '(?:[0]?[1-9]|[1][012])',
|
|
||||||
'DD': '(?:' + $.fn.datepicker.dates[language].days.join('|') + ')',
|
|
||||||
'D': '(?:' + $.fn.datepicker.dates[language].daysShort.join('|') + ')',
|
|
||||||
'MM': '(?:' + $.fn.datepicker.dates[language].months.join('|') + ')',
|
|
||||||
'M': '(?:' + $.fn.datepicker.dates[language].monthsShort.join('|') + ')',
|
|
||||||
'yyyy': '(?:(?:[1]{1}[0-9]{1}[0-9]{1}[0-9]{1})|(?:[2]{1}[0-9]{3}))(?![[0-9]])',
|
|
||||||
'yy': '(?:(?:[0-9]{1}[0-9]{1}))(?![[0-9]])'
|
|
||||||
};
|
|
||||||
};
|
|
||||||
var regexpForDateFormat = function regexpForDateFormat(format, language) {
|
|
||||||
var re = format, map = regexpMap(language), i;
|
|
||||||
i = 0;
|
|
||||||
angular.forEach(map, function (v, k) {
|
|
||||||
re = re.split(k).join('${' + i + '}');
|
|
||||||
i++;
|
|
||||||
});
|
|
||||||
i = 0;
|
|
||||||
angular.forEach(map, function (v, k) {
|
|
||||||
re = re.split('${' + i + '}').join(v);
|
|
||||||
i++;
|
|
||||||
});
|
|
||||||
return new RegExp('^' + re + '$', ['i']);
|
|
||||||
};
|
|
||||||
return {
|
|
||||||
restrict: 'A',
|
|
||||||
require: '?ngModel',
|
|
||||||
link: function postLink(scope, element, attrs, controller) {
|
|
||||||
var options = angular.extend({ autoclose: true }, $strapConfig.datepicker || {}), type = attrs.dateType || options.type || 'date';
|
|
||||||
angular.forEach([
|
|
||||||
'format',
|
|
||||||
'weekStart',
|
|
||||||
'calendarWeeks',
|
|
||||||
'startDate',
|
|
||||||
'endDate',
|
|
||||||
'daysOfWeekDisabled',
|
|
||||||
'autoclose',
|
|
||||||
'startView',
|
|
||||||
'minViewMode',
|
|
||||||
'todayBtn',
|
|
||||||
'todayHighlight',
|
|
||||||
'keyboardNavigation',
|
|
||||||
'language',
|
|
||||||
'forceParse'
|
|
||||||
], function (key) {
|
|
||||||
if (angular.isDefined(attrs[key]))
|
|
||||||
options[key] = attrs[key];
|
|
||||||
});
|
|
||||||
var language = options.language || 'en', readFormat = attrs.dateFormat || options.format || $.fn.datepicker.dates[language] && $.fn.datepicker.dates[language].format || 'mm/dd/yyyy', format = isAppleTouch ? 'yyyy-mm-dd' : readFormat, dateFormatRegexp = regexpForDateFormat(format, language);
|
|
||||||
if (controller) {
|
|
||||||
controller.$formatters.unshift(function (modelValue) {
|
|
||||||
return type === 'date' && angular.isString(modelValue) && modelValue ? $.fn.datepicker.DPGlobal.parseDate(new Date(modelValue), $.fn.datepicker.DPGlobal.parseFormat(readFormat), language) : modelValue;
|
|
||||||
});
|
|
||||||
controller.$parsers.unshift(function (viewValue) {
|
|
||||||
if (!viewValue) {
|
|
||||||
controller.$setValidity('date', true);
|
|
||||||
return null;
|
|
||||||
} else if (type === 'date' && angular.isDate(viewValue)) {
|
|
||||||
controller.$setValidity('date', true);
|
|
||||||
return viewValue;
|
|
||||||
} else if (angular.isString(viewValue) && dateFormatRegexp.test(viewValue)) {
|
|
||||||
controller.$setValidity('date', true);
|
|
||||||
if (isAppleTouch)
|
|
||||||
return new Date(viewValue);
|
|
||||||
return type === 'string' ? viewValue : $.fn.datepicker.DPGlobal.parseDate(viewValue, $.fn.datepicker.DPGlobal.parseFormat(format), language);
|
|
||||||
} else {
|
|
||||||
controller.$setValidity('date', false);
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
controller.$render = function ngModelRender() {
|
|
||||||
if (isAppleTouch) {
|
|
||||||
var date = controller.$viewValue ? $.fn.datepicker.DPGlobal.formatDate(controller.$viewValue, $.fn.datepicker.DPGlobal.parseFormat(format), language) : '';
|
|
||||||
element.val(date);
|
|
||||||
return date;
|
|
||||||
}
|
|
||||||
if (!controller.$viewValue)
|
|
||||||
element.val('');
|
|
||||||
return element.datepicker('update', controller.$viewValue);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (isAppleTouch) {
|
|
||||||
element.prop('type', 'date').css('-webkit-appearance', 'textfield');
|
|
||||||
} else {
|
|
||||||
if (controller) {
|
|
||||||
element.on('changeDate', function (ev) {
|
|
||||||
scope.$apply(function () {
|
|
||||||
controller.$setViewValue(type === 'string' ? element.val() : ev.date);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
element.datepicker(angular.extend(options, {
|
|
||||||
format: format,
|
|
||||||
language: language
|
|
||||||
}));
|
|
||||||
scope.$on('$destroy', function () {
|
|
||||||
var datepicker = element.data('datepicker');
|
|
||||||
if (datepicker) {
|
|
||||||
datepicker.picker.remove();
|
|
||||||
element.data('datepicker', null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
attrs.$observe('startDate', function (value) {
|
|
||||||
element.datepicker('setStartDate', value);
|
|
||||||
});
|
|
||||||
attrs.$observe('endDate', function (value) {
|
|
||||||
element.datepicker('setEndDate', value);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
var component = element.siblings('[data-toggle="datepicker"]');
|
|
||||||
if (component.length) {
|
|
||||||
component.on('click', function () {
|
|
||||||
if (!element.prop('disabled')) {
|
|
||||||
element.trigger('focus');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
'use strict';
|
|
||||||
angular.module('$strap.directives').factory('$modal', [
|
angular.module('$strap.directives').factory('$modal', [
|
||||||
'$rootScope',
|
'$rootScope',
|
||||||
'$compile',
|
'$compile',
|
||||||
|
212
public/vendor/angular-ui/dateparser.js
vendored
Normal file
212
public/vendor/angular-ui/dateparser.js
vendored
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
angular.module('ui.bootstrap.dateparser', [])
|
||||||
|
|
||||||
|
.service('dateParser', ['$log', '$locale', 'orderByFilter', function($log, $locale, orderByFilter) {
|
||||||
|
// Pulled from https://github.com/mbostock/d3/blob/master/src/format/requote.js
|
||||||
|
var SPECIAL_CHARACTERS_REGEXP = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;
|
||||||
|
|
||||||
|
var localeId;
|
||||||
|
var formatCodeToRegex;
|
||||||
|
|
||||||
|
this.init = function() {
|
||||||
|
localeId = $locale.id;
|
||||||
|
|
||||||
|
this.parsers = {};
|
||||||
|
|
||||||
|
formatCodeToRegex = {
|
||||||
|
'yyyy': {
|
||||||
|
regex: '\\d{4}',
|
||||||
|
apply: function(value) { this.year = +value; }
|
||||||
|
},
|
||||||
|
'yy': {
|
||||||
|
regex: '\\d{2}',
|
||||||
|
apply: function(value) { this.year = +value + 2000; }
|
||||||
|
},
|
||||||
|
'y': {
|
||||||
|
regex: '\\d{1,4}',
|
||||||
|
apply: function(value) { this.year = +value; }
|
||||||
|
},
|
||||||
|
'MMMM': {
|
||||||
|
regex: $locale.DATETIME_FORMATS.MONTH.join('|'),
|
||||||
|
apply: function(value) { this.month = $locale.DATETIME_FORMATS.MONTH.indexOf(value); }
|
||||||
|
},
|
||||||
|
'MMM': {
|
||||||
|
regex: $locale.DATETIME_FORMATS.SHORTMONTH.join('|'),
|
||||||
|
apply: function(value) { this.month = $locale.DATETIME_FORMATS.SHORTMONTH.indexOf(value); }
|
||||||
|
},
|
||||||
|
'MM': {
|
||||||
|
regex: '0[1-9]|1[0-2]',
|
||||||
|
apply: function(value) { this.month = value - 1; }
|
||||||
|
},
|
||||||
|
'M': {
|
||||||
|
regex: '[1-9]|1[0-2]',
|
||||||
|
apply: function(value) { this.month = value - 1; }
|
||||||
|
},
|
||||||
|
'dd': {
|
||||||
|
regex: '[0-2][0-9]{1}|3[0-1]{1}',
|
||||||
|
apply: function(value) { this.date = +value; }
|
||||||
|
},
|
||||||
|
'd': {
|
||||||
|
regex: '[1-2]?[0-9]{1}|3[0-1]{1}',
|
||||||
|
apply: function(value) { this.date = +value; }
|
||||||
|
},
|
||||||
|
'EEEE': {
|
||||||
|
regex: $locale.DATETIME_FORMATS.DAY.join('|')
|
||||||
|
},
|
||||||
|
'EEE': {
|
||||||
|
regex: $locale.DATETIME_FORMATS.SHORTDAY.join('|')
|
||||||
|
},
|
||||||
|
'HH': {
|
||||||
|
regex: '(?:0|1)[0-9]|2[0-3]',
|
||||||
|
apply: function(value) { this.hours = +value; }
|
||||||
|
},
|
||||||
|
'hh': {
|
||||||
|
regex: '0[0-9]|1[0-2]',
|
||||||
|
apply: function(value) { this.hours = +value; }
|
||||||
|
},
|
||||||
|
'H': {
|
||||||
|
regex: '1?[0-9]|2[0-3]',
|
||||||
|
apply: function(value) { this.hours = +value; }
|
||||||
|
},
|
||||||
|
'h': {
|
||||||
|
regex: '[0-9]|1[0-2]',
|
||||||
|
apply: function(value) { this.hours = +value; }
|
||||||
|
},
|
||||||
|
'mm': {
|
||||||
|
regex: '[0-5][0-9]',
|
||||||
|
apply: function(value) { this.minutes = +value; }
|
||||||
|
},
|
||||||
|
'm': {
|
||||||
|
regex: '[0-9]|[1-5][0-9]',
|
||||||
|
apply: function(value) { this.minutes = +value; }
|
||||||
|
},
|
||||||
|
'sss': {
|
||||||
|
regex: '[0-9][0-9][0-9]',
|
||||||
|
apply: function(value) { this.milliseconds = +value; }
|
||||||
|
},
|
||||||
|
'ss': {
|
||||||
|
regex: '[0-5][0-9]',
|
||||||
|
apply: function(value) { this.seconds = +value; }
|
||||||
|
},
|
||||||
|
's': {
|
||||||
|
regex: '[0-9]|[1-5][0-9]',
|
||||||
|
apply: function(value) { this.seconds = +value; }
|
||||||
|
},
|
||||||
|
'a': {
|
||||||
|
regex: $locale.DATETIME_FORMATS.AMPMS.join('|'),
|
||||||
|
apply: function(value) {
|
||||||
|
if (this.hours === 12) {
|
||||||
|
this.hours = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value === 'PM') {
|
||||||
|
this.hours += 12;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
this.init();
|
||||||
|
|
||||||
|
function createParser(format) {
|
||||||
|
var map = [], regex = format.split('');
|
||||||
|
|
||||||
|
angular.forEach(formatCodeToRegex, function(data, code) {
|
||||||
|
var index = format.indexOf(code);
|
||||||
|
|
||||||
|
if (index > -1) {
|
||||||
|
format = format.split('');
|
||||||
|
|
||||||
|
regex[index] = '(' + data.regex + ')';
|
||||||
|
format[index] = '$'; // Custom symbol to define consumed part of format
|
||||||
|
for (var i = index + 1, n = index + code.length; i < n; i++) {
|
||||||
|
regex[i] = '';
|
||||||
|
format[i] = '$';
|
||||||
|
}
|
||||||
|
format = format.join('');
|
||||||
|
|
||||||
|
map.push({ index: index, apply: data.apply });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
regex: new RegExp('^' + regex.join('') + '$'),
|
||||||
|
map: orderByFilter(map, 'index')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
this.parse = function(input, format, baseDate) {
|
||||||
|
if (!angular.isString(input) || !format) {
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
format = $locale.DATETIME_FORMATS[format] || format;
|
||||||
|
format = format.replace(SPECIAL_CHARACTERS_REGEXP, '\\$&');
|
||||||
|
|
||||||
|
if ($locale.id !== localeId) {
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.parsers[format]) {
|
||||||
|
this.parsers[format] = createParser(format);
|
||||||
|
}
|
||||||
|
|
||||||
|
var parser = this.parsers[format],
|
||||||
|
regex = parser.regex,
|
||||||
|
map = parser.map,
|
||||||
|
results = input.match(regex);
|
||||||
|
|
||||||
|
if (results && results.length) {
|
||||||
|
var fields, dt;
|
||||||
|
if (angular.isDate(baseDate) && !isNaN(baseDate.getTime())) {
|
||||||
|
fields = {
|
||||||
|
year: baseDate.getFullYear(),
|
||||||
|
month: baseDate.getMonth(),
|
||||||
|
date: baseDate.getDate(),
|
||||||
|
hours: baseDate.getHours(),
|
||||||
|
minutes: baseDate.getMinutes(),
|
||||||
|
seconds: baseDate.getSeconds(),
|
||||||
|
milliseconds: baseDate.getMilliseconds()
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
if (baseDate) {
|
||||||
|
$log.warn('dateparser:', 'baseDate is not a valid date');
|
||||||
|
}
|
||||||
|
fields = { year: 1900, month: 0, date: 1, hours: 0, minutes: 0, seconds: 0, milliseconds: 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 1, n = results.length; i < n; i++) {
|
||||||
|
var mapper = map[i-1];
|
||||||
|
if (mapper.apply) {
|
||||||
|
mapper.apply.call(fields, results[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isValid(fields.year, fields.month, fields.date)) {
|
||||||
|
dt = new Date(fields.year, fields.month, fields.date,
|
||||||
|
fields.hours, fields.minutes, fields.seconds,
|
||||||
|
fields.milliseconds || 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dt;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check if date is valid for specific month (and year for February).
|
||||||
|
// Month: 0 = Jan, 1 = Feb, etc
|
||||||
|
function isValid(year, month, date) {
|
||||||
|
if (date < 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (month === 1 && date > 28) {
|
||||||
|
return date === 29 && ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (month === 3 || month === 5 || month === 8 || month === 10) {
|
||||||
|
return date < 31;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}]);
|
823
public/vendor/angular-ui/datepicker.js
vendored
Normal file
823
public/vendor/angular-ui/datepicker.js
vendored
Normal file
@ -0,0 +1,823 @@
|
|||||||
|
angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootstrap.position'])
|
||||||
|
|
||||||
|
.value('$datepickerSuppressError', false)
|
||||||
|
|
||||||
|
.constant('datepickerConfig', {
|
||||||
|
formatDay: 'dd',
|
||||||
|
formatMonth: 'MMMM',
|
||||||
|
formatYear: 'yyyy',
|
||||||
|
formatDayHeader: 'EEE',
|
||||||
|
formatDayTitle: 'MMMM yyyy',
|
||||||
|
formatMonthTitle: 'yyyy',
|
||||||
|
datepickerMode: 'day',
|
||||||
|
minMode: 'day',
|
||||||
|
maxMode: 'year',
|
||||||
|
showWeeks: true,
|
||||||
|
startingDay: 0,
|
||||||
|
yearRange: 20,
|
||||||
|
minDate: null,
|
||||||
|
maxDate: null,
|
||||||
|
shortcutPropagation: false
|
||||||
|
})
|
||||||
|
|
||||||
|
.controller('DatepickerController', ['$scope', '$attrs', '$parse', '$interpolate', '$log', 'dateFilter', 'datepickerConfig', '$datepickerSuppressError', function($scope, $attrs, $parse, $interpolate, $log, dateFilter, datepickerConfig, $datepickerSuppressError) {
|
||||||
|
var self = this,
|
||||||
|
ngModelCtrl = { $setViewValue: angular.noop }; // nullModelCtrl;
|
||||||
|
|
||||||
|
// Modes chain
|
||||||
|
this.modes = ['day', 'month', 'year'];
|
||||||
|
|
||||||
|
// Configuration attributes
|
||||||
|
angular.forEach(['formatDay', 'formatMonth', 'formatYear', 'formatDayHeader', 'formatDayTitle', 'formatMonthTitle',
|
||||||
|
'showWeeks', 'startingDay', 'yearRange', 'shortcutPropagation'], function(key, index) {
|
||||||
|
self[key] = angular.isDefined($attrs[key]) ? (index < 6 ? $interpolate($attrs[key])($scope.$parent) : $scope.$parent.$eval($attrs[key])) : datepickerConfig[key];
|
||||||
|
});
|
||||||
|
|
||||||
|
// Watchable date attributes
|
||||||
|
angular.forEach(['minDate', 'maxDate'], function(key) {
|
||||||
|
if ($attrs[key]) {
|
||||||
|
$scope.$parent.$watch($parse($attrs[key]), function(value) {
|
||||||
|
self[key] = value ? new Date(value) : null;
|
||||||
|
self.refreshView();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
self[key] = datepickerConfig[key] ? new Date(datepickerConfig[key]) : null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
angular.forEach(['minMode', 'maxMode'], function(key) {
|
||||||
|
if ($attrs[key]) {
|
||||||
|
$scope.$parent.$watch($parse($attrs[key]), function(value) {
|
||||||
|
self[key] = angular.isDefined(value) ? value : $attrs[key];
|
||||||
|
$scope[key] = self[key];
|
||||||
|
if ((key == 'minMode' && self.modes.indexOf($scope.datepickerMode) < self.modes.indexOf(self[key])) || (key == 'maxMode' && self.modes.indexOf($scope.datepickerMode) > self.modes.indexOf(self[key]))) {
|
||||||
|
$scope.datepickerMode = self[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
self[key] = datepickerConfig[key] || null;
|
||||||
|
$scope[key] = self[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$scope.datepickerMode = $scope.datepickerMode || datepickerConfig.datepickerMode;
|
||||||
|
$scope.uniqueId = 'datepicker-' + $scope.$id + '-' + Math.floor(Math.random() * 10000);
|
||||||
|
|
||||||
|
if (angular.isDefined($attrs.initDate)) {
|
||||||
|
this.activeDate = $scope.$parent.$eval($attrs.initDate) || new Date();
|
||||||
|
$scope.$parent.$watch($attrs.initDate, function(initDate) {
|
||||||
|
if (initDate && (ngModelCtrl.$isEmpty(ngModelCtrl.$modelValue) || ngModelCtrl.$invalid)) {
|
||||||
|
self.activeDate = initDate;
|
||||||
|
self.refreshView();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.activeDate = new Date();
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.isActive = function(dateObject) {
|
||||||
|
if (self.compare(dateObject.date, self.activeDate) === 0) {
|
||||||
|
$scope.activeDateId = dateObject.uid;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.init = function(ngModelCtrl_) {
|
||||||
|
ngModelCtrl = ngModelCtrl_;
|
||||||
|
|
||||||
|
ngModelCtrl.$render = function() {
|
||||||
|
self.render();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
this.render = function() {
|
||||||
|
if (ngModelCtrl.$viewValue) {
|
||||||
|
var date = new Date(ngModelCtrl.$viewValue),
|
||||||
|
isValid = !isNaN(date);
|
||||||
|
|
||||||
|
if (isValid) {
|
||||||
|
this.activeDate = date;
|
||||||
|
} else if (!$datepickerSuppressError) {
|
||||||
|
$log.error('Datepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.refreshView();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.refreshView = function() {
|
||||||
|
if (this.element) {
|
||||||
|
this._refreshView();
|
||||||
|
|
||||||
|
var date = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null;
|
||||||
|
ngModelCtrl.$setValidity('dateDisabled', !date || (this.element && !this.isDisabled(date)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.createDateObject = function(date, format) {
|
||||||
|
var model = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null;
|
||||||
|
return {
|
||||||
|
date: date,
|
||||||
|
label: dateFilter(date, format),
|
||||||
|
selected: model && this.compare(date, model) === 0,
|
||||||
|
disabled: this.isDisabled(date),
|
||||||
|
current: this.compare(date, new Date()) === 0,
|
||||||
|
customClass: this.customClass(date)
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
this.isDisabled = function(date) {
|
||||||
|
return ((this.minDate && this.compare(date, this.minDate) < 0) || (this.maxDate && this.compare(date, this.maxDate) > 0) || ($attrs.dateDisabled && $scope.dateDisabled({date: date, mode: $scope.datepickerMode})));
|
||||||
|
};
|
||||||
|
|
||||||
|
this.customClass = function(date) {
|
||||||
|
return $scope.customClass({date: date, mode: $scope.datepickerMode});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Split array into smaller arrays
|
||||||
|
this.split = function(arr, size) {
|
||||||
|
var arrays = [];
|
||||||
|
while (arr.length > 0) {
|
||||||
|
arrays.push(arr.splice(0, size));
|
||||||
|
}
|
||||||
|
return arrays;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fix a hard-reprodusible bug with timezones
|
||||||
|
// The bug depends on OS, browser, current timezone and current date
|
||||||
|
// i.e.
|
||||||
|
// var date = new Date(2014, 0, 1);
|
||||||
|
// console.log(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours());
|
||||||
|
// can result in "2013 11 31 23" because of the bug.
|
||||||
|
this.fixTimeZone = function(date) {
|
||||||
|
var hours = date.getHours();
|
||||||
|
date.setHours(hours === 23 ? hours + 2 : 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.select = function(date) {
|
||||||
|
if ($scope.datepickerMode === self.minMode) {
|
||||||
|
var dt = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : new Date(0, 0, 0, 0, 0, 0, 0);
|
||||||
|
dt.setFullYear(date.getFullYear(), date.getMonth(), date.getDate());
|
||||||
|
ngModelCtrl.$setViewValue(dt);
|
||||||
|
ngModelCtrl.$render();
|
||||||
|
} else {
|
||||||
|
self.activeDate = date;
|
||||||
|
$scope.datepickerMode = self.modes[self.modes.indexOf($scope.datepickerMode) - 1];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.move = function(direction) {
|
||||||
|
var year = self.activeDate.getFullYear() + direction * (self.step.years || 0),
|
||||||
|
month = self.activeDate.getMonth() + direction * (self.step.months || 0);
|
||||||
|
self.activeDate.setFullYear(year, month, 1);
|
||||||
|
self.refreshView();
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.toggleMode = function(direction) {
|
||||||
|
direction = direction || 1;
|
||||||
|
|
||||||
|
if (($scope.datepickerMode === self.maxMode && direction === 1) || ($scope.datepickerMode === self.minMode && direction === -1)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.datepickerMode = self.modes[self.modes.indexOf($scope.datepickerMode) + direction];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Key event mapper
|
||||||
|
$scope.keys = { 13: 'enter', 32: 'space', 33: 'pageup', 34: 'pagedown', 35: 'end', 36: 'home', 37: 'left', 38: 'up', 39: 'right', 40: 'down' };
|
||||||
|
|
||||||
|
var focusElement = function() {
|
||||||
|
self.element[0].focus();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Listen for focus requests from popup directive
|
||||||
|
$scope.$on('datepicker.focus', focusElement);
|
||||||
|
|
||||||
|
$scope.keydown = function(evt) {
|
||||||
|
var key = $scope.keys[evt.which];
|
||||||
|
|
||||||
|
if (!key || evt.shiftKey || evt.altKey) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
evt.preventDefault();
|
||||||
|
if (!self.shortcutPropagation) {
|
||||||
|
evt.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === 'enter' || key === 'space') {
|
||||||
|
if (self.isDisabled(self.activeDate)) {
|
||||||
|
return; // do nothing
|
||||||
|
}
|
||||||
|
$scope.select(self.activeDate);
|
||||||
|
} else if (evt.ctrlKey && (key === 'up' || key === 'down')) {
|
||||||
|
$scope.toggleMode(key === 'up' ? 1 : -1);
|
||||||
|
} else {
|
||||||
|
self.handleKeyDown(key, evt);
|
||||||
|
self.refreshView();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}])
|
||||||
|
|
||||||
|
.directive('datepicker', function() {
|
||||||
|
return {
|
||||||
|
restrict: 'EA',
|
||||||
|
replace: true,
|
||||||
|
templateUrl: function(element, attrs) {
|
||||||
|
return attrs.templateUrl || 'template/datepicker/datepicker.html';
|
||||||
|
},
|
||||||
|
scope: {
|
||||||
|
datepickerMode: '=?',
|
||||||
|
dateDisabled: '&',
|
||||||
|
customClass: '&',
|
||||||
|
shortcutPropagation: '&?'
|
||||||
|
},
|
||||||
|
require: ['datepicker', '^ngModel'],
|
||||||
|
controller: 'DatepickerController',
|
||||||
|
controllerAs: 'datepicker',
|
||||||
|
link: function(scope, element, attrs, ctrls) {
|
||||||
|
var datepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1];
|
||||||
|
|
||||||
|
datepickerCtrl.init(ngModelCtrl);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})
|
||||||
|
|
||||||
|
.directive('daypicker', ['dateFilter', function(dateFilter) {
|
||||||
|
return {
|
||||||
|
restrict: 'EA',
|
||||||
|
replace: true,
|
||||||
|
templateUrl: 'template/datepicker/day.html',
|
||||||
|
require: '^datepicker',
|
||||||
|
link: function(scope, element, attrs, ctrl) {
|
||||||
|
scope.showWeeks = ctrl.showWeeks;
|
||||||
|
|
||||||
|
ctrl.step = { months: 1 };
|
||||||
|
ctrl.element = element;
|
||||||
|
|
||||||
|
var DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
||||||
|
function getDaysInMonth(year, month) {
|
||||||
|
return ((month === 1) && (year % 4 === 0) && ((year % 100 !== 0) || (year % 400 === 0))) ? 29 : DAYS_IN_MONTH[month];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDates(startDate, n) {
|
||||||
|
var dates = new Array(n), current = new Date(startDate), i = 0, date;
|
||||||
|
while (i < n) {
|
||||||
|
date = new Date(current);
|
||||||
|
ctrl.fixTimeZone(date);
|
||||||
|
dates[i++] = date;
|
||||||
|
current.setDate(current.getDate() + 1);
|
||||||
|
}
|
||||||
|
return dates;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctrl._refreshView = function() {
|
||||||
|
var year = ctrl.activeDate.getFullYear(),
|
||||||
|
month = ctrl.activeDate.getMonth(),
|
||||||
|
firstDayOfMonth = new Date(year, month, 1),
|
||||||
|
difference = ctrl.startingDay - firstDayOfMonth.getDay(),
|
||||||
|
numDisplayedFromPreviousMonth = (difference > 0) ? 7 - difference : - difference,
|
||||||
|
firstDate = new Date(firstDayOfMonth);
|
||||||
|
|
||||||
|
if (numDisplayedFromPreviousMonth > 0) {
|
||||||
|
firstDate.setDate(-numDisplayedFromPreviousMonth + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 42 is the number of days on a six-month calendar
|
||||||
|
var days = getDates(firstDate, 42);
|
||||||
|
for (var i = 0; i < 42; i ++) {
|
||||||
|
days[i] = angular.extend(ctrl.createDateObject(days[i], ctrl.formatDay), {
|
||||||
|
secondary: days[i].getMonth() !== month,
|
||||||
|
uid: scope.uniqueId + '-' + i
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.labels = new Array(7);
|
||||||
|
for (var j = 0; j < 7; j++) {
|
||||||
|
scope.labels[j] = {
|
||||||
|
abbr: dateFilter(days[j].date, ctrl.formatDayHeader),
|
||||||
|
full: dateFilter(days[j].date, 'EEEE')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.title = dateFilter(ctrl.activeDate, ctrl.formatDayTitle);
|
||||||
|
scope.rows = ctrl.split(days, 7);
|
||||||
|
|
||||||
|
if (scope.showWeeks) {
|
||||||
|
scope.weekNumbers = [];
|
||||||
|
var thursdayIndex = (4 + 7 - ctrl.startingDay) % 7,
|
||||||
|
numWeeks = scope.rows.length;
|
||||||
|
for (var curWeek = 0; curWeek < numWeeks; curWeek++) {
|
||||||
|
scope.weekNumbers.push(
|
||||||
|
getISO8601WeekNumber(scope.rows[curWeek][thursdayIndex].date));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ctrl.compare = function(date1, date2) {
|
||||||
|
return (new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()) - new Date(date2.getFullYear(), date2.getMonth(), date2.getDate()));
|
||||||
|
};
|
||||||
|
|
||||||
|
function getISO8601WeekNumber(date) {
|
||||||
|
var checkDate = new Date(date);
|
||||||
|
checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7)); // Thursday
|
||||||
|
var time = checkDate.getTime();
|
||||||
|
checkDate.setMonth(0); // Compare with Jan 1
|
||||||
|
checkDate.setDate(1);
|
||||||
|
return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctrl.handleKeyDown = function(key, evt) {
|
||||||
|
var date = ctrl.activeDate.getDate();
|
||||||
|
|
||||||
|
if (key === 'left') {
|
||||||
|
date = date - 1; // up
|
||||||
|
} else if (key === 'up') {
|
||||||
|
date = date - 7; // down
|
||||||
|
} else if (key === 'right') {
|
||||||
|
date = date + 1; // down
|
||||||
|
} else if (key === 'down') {
|
||||||
|
date = date + 7;
|
||||||
|
} else if (key === 'pageup' || key === 'pagedown') {
|
||||||
|
var month = ctrl.activeDate.getMonth() + (key === 'pageup' ? - 1 : 1);
|
||||||
|
ctrl.activeDate.setMonth(month, 1);
|
||||||
|
date = Math.min(getDaysInMonth(ctrl.activeDate.getFullYear(), ctrl.activeDate.getMonth()), date);
|
||||||
|
} else if (key === 'home') {
|
||||||
|
date = 1;
|
||||||
|
} else if (key === 'end') {
|
||||||
|
date = getDaysInMonth(ctrl.activeDate.getFullYear(), ctrl.activeDate.getMonth());
|
||||||
|
}
|
||||||
|
ctrl.activeDate.setDate(date);
|
||||||
|
};
|
||||||
|
|
||||||
|
ctrl.refreshView();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}])
|
||||||
|
|
||||||
|
.directive('monthpicker', ['dateFilter', function(dateFilter) {
|
||||||
|
return {
|
||||||
|
restrict: 'EA',
|
||||||
|
replace: true,
|
||||||
|
templateUrl: 'template/datepicker/month.html',
|
||||||
|
require: '^datepicker',
|
||||||
|
link: function(scope, element, attrs, ctrl) {
|
||||||
|
ctrl.step = { years: 1 };
|
||||||
|
ctrl.element = element;
|
||||||
|
|
||||||
|
ctrl._refreshView = function() {
|
||||||
|
var months = new Array(12),
|
||||||
|
year = ctrl.activeDate.getFullYear(),
|
||||||
|
date;
|
||||||
|
|
||||||
|
for (var i = 0; i < 12; i++) {
|
||||||
|
date = new Date(year, i, 1);
|
||||||
|
ctrl.fixTimeZone(date);
|
||||||
|
months[i] = angular.extend(ctrl.createDateObject(date, ctrl.formatMonth), {
|
||||||
|
uid: scope.uniqueId + '-' + i
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.title = dateFilter(ctrl.activeDate, ctrl.formatMonthTitle);
|
||||||
|
scope.rows = ctrl.split(months, 3);
|
||||||
|
};
|
||||||
|
|
||||||
|
ctrl.compare = function(date1, date2) {
|
||||||
|
return new Date(date1.getFullYear(), date1.getMonth()) - new Date(date2.getFullYear(), date2.getMonth());
|
||||||
|
};
|
||||||
|
|
||||||
|
ctrl.handleKeyDown = function(key, evt) {
|
||||||
|
var date = ctrl.activeDate.getMonth();
|
||||||
|
|
||||||
|
if (key === 'left') {
|
||||||
|
date = date - 1; // up
|
||||||
|
} else if (key === 'up') {
|
||||||
|
date = date - 3; // down
|
||||||
|
} else if (key === 'right') {
|
||||||
|
date = date + 1; // down
|
||||||
|
} else if (key === 'down') {
|
||||||
|
date = date + 3;
|
||||||
|
} else if (key === 'pageup' || key === 'pagedown') {
|
||||||
|
var year = ctrl.activeDate.getFullYear() + (key === 'pageup' ? - 1 : 1);
|
||||||
|
ctrl.activeDate.setFullYear(year);
|
||||||
|
} else if (key === 'home') {
|
||||||
|
date = 0;
|
||||||
|
} else if (key === 'end') {
|
||||||
|
date = 11;
|
||||||
|
}
|
||||||
|
ctrl.activeDate.setMonth(date);
|
||||||
|
};
|
||||||
|
|
||||||
|
ctrl.refreshView();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}])
|
||||||
|
|
||||||
|
.directive('yearpicker', ['dateFilter', function(dateFilter) {
|
||||||
|
return {
|
||||||
|
restrict: 'EA',
|
||||||
|
replace: true,
|
||||||
|
templateUrl: 'template/datepicker/year.html',
|
||||||
|
require: '^datepicker',
|
||||||
|
link: function(scope, element, attrs, ctrl) {
|
||||||
|
var range = ctrl.yearRange;
|
||||||
|
|
||||||
|
ctrl.step = { years: range };
|
||||||
|
ctrl.element = element;
|
||||||
|
|
||||||
|
function getStartingYear( year ) {
|
||||||
|
return parseInt((year - 1) / range, 10) * range + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctrl._refreshView = function() {
|
||||||
|
var years = new Array(range), date;
|
||||||
|
|
||||||
|
for (var i = 0, start = getStartingYear(ctrl.activeDate.getFullYear()); i < range; i++) {
|
||||||
|
date = new Date(start + i, 0, 1);
|
||||||
|
ctrl.fixTimeZone(date);
|
||||||
|
years[i] = angular.extend(ctrl.createDateObject(date, ctrl.formatYear), {
|
||||||
|
uid: scope.uniqueId + '-' + i
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.title = [years[0].label, years[range - 1].label].join(' - ');
|
||||||
|
scope.rows = ctrl.split(years, 5);
|
||||||
|
};
|
||||||
|
|
||||||
|
ctrl.compare = function(date1, date2) {
|
||||||
|
return date1.getFullYear() - date2.getFullYear();
|
||||||
|
};
|
||||||
|
|
||||||
|
ctrl.handleKeyDown = function(key, evt) {
|
||||||
|
var date = ctrl.activeDate.getFullYear();
|
||||||
|
|
||||||
|
if (key === 'left') {
|
||||||
|
date = date - 1; // up
|
||||||
|
} else if (key === 'up') {
|
||||||
|
date = date - 5; // down
|
||||||
|
} else if (key === 'right') {
|
||||||
|
date = date + 1; // down
|
||||||
|
} else if (key === 'down') {
|
||||||
|
date = date + 5;
|
||||||
|
} else if (key === 'pageup' || key === 'pagedown') {
|
||||||
|
date += (key === 'pageup' ? - 1 : 1) * ctrl.step.years;
|
||||||
|
} else if (key === 'home') {
|
||||||
|
date = getStartingYear(ctrl.activeDate.getFullYear());
|
||||||
|
} else if (key === 'end') {
|
||||||
|
date = getStartingYear(ctrl.activeDate.getFullYear()) + range - 1;
|
||||||
|
}
|
||||||
|
ctrl.activeDate.setFullYear(date);
|
||||||
|
};
|
||||||
|
|
||||||
|
ctrl.refreshView();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}])
|
||||||
|
|
||||||
|
.constant('datepickerPopupConfig', {
|
||||||
|
datepickerPopup: 'yyyy-MM-dd',
|
||||||
|
datepickerPopupTemplateUrl: 'template/datepicker/popup.html',
|
||||||
|
datepickerTemplateUrl: 'template/datepicker/datepicker.html',
|
||||||
|
html5Types: {
|
||||||
|
date: 'yyyy-MM-dd',
|
||||||
|
'datetime-local': 'yyyy-MM-ddTHH:mm:ss.sss',
|
||||||
|
'month': 'yyyy-MM'
|
||||||
|
},
|
||||||
|
currentText: 'Today',
|
||||||
|
clearText: 'Clear',
|
||||||
|
closeText: 'Done',
|
||||||
|
closeOnDateSelection: true,
|
||||||
|
appendToBody: false,
|
||||||
|
showButtonBar: true,
|
||||||
|
onOpenFocus: true
|
||||||
|
})
|
||||||
|
|
||||||
|
.directive('datepickerPopup', ['$compile', '$parse', '$document', '$rootScope', '$position', 'dateFilter', 'dateParser', 'datepickerPopupConfig', '$timeout',
|
||||||
|
function($compile, $parse, $document, $rootScope, $position, dateFilter, dateParser, datepickerPopupConfig, $timeout) {
|
||||||
|
return {
|
||||||
|
restrict: 'EA',
|
||||||
|
require: 'ngModel',
|
||||||
|
scope: {
|
||||||
|
isOpen: '=?',
|
||||||
|
currentText: '@',
|
||||||
|
clearText: '@',
|
||||||
|
closeText: '@',
|
||||||
|
dateDisabled: '&',
|
||||||
|
customClass: '&'
|
||||||
|
},
|
||||||
|
link: function(scope, element, attrs, ngModel) {
|
||||||
|
var dateFormat,
|
||||||
|
closeOnDateSelection = angular.isDefined(attrs.closeOnDateSelection) ? scope.$parent.$eval(attrs.closeOnDateSelection) : datepickerPopupConfig.closeOnDateSelection,
|
||||||
|
appendToBody = angular.isDefined(attrs.datepickerAppendToBody) ? scope.$parent.$eval(attrs.datepickerAppendToBody) : datepickerPopupConfig.appendToBody,
|
||||||
|
onOpenFocus = angular.isDefined(attrs.onOpenFocus) ? scope.$parent.$eval(attrs.onOpenFocus) : datepickerPopupConfig.onOpenFocus,
|
||||||
|
datepickerPopupTemplateUrl = angular.isDefined(attrs.datepickerPopupTemplateUrl) ? attrs.datepickerPopupTemplateUrl : datepickerPopupConfig.datepickerPopupTemplateUrl,
|
||||||
|
datepickerTemplateUrl = angular.isDefined(attrs.datepickerTemplateUrl) ? attrs.datepickerTemplateUrl : datepickerPopupConfig.datepickerTemplateUrl,
|
||||||
|
cache = {};
|
||||||
|
|
||||||
|
scope.showButtonBar = angular.isDefined(attrs.showButtonBar) ? scope.$parent.$eval(attrs.showButtonBar) : datepickerPopupConfig.showButtonBar;
|
||||||
|
|
||||||
|
scope.getText = function(key) {
|
||||||
|
return scope[key + 'Text'] || datepickerPopupConfig[key + 'Text'];
|
||||||
|
};
|
||||||
|
|
||||||
|
scope.isDisabled = function(date) {
|
||||||
|
if (date === 'today') {
|
||||||
|
date = new Date();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ((scope.watchData.minDate && scope.compare(date, cache.minDate) < 0) ||
|
||||||
|
(scope.watchData.maxDate && scope.compare(date, cache.maxDate) > 0));
|
||||||
|
};
|
||||||
|
|
||||||
|
scope.compare = function(date1, date2) {
|
||||||
|
return (new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()) - new Date(date2.getFullYear(), date2.getMonth(), date2.getDate()));
|
||||||
|
};
|
||||||
|
|
||||||
|
var isHtml5DateInput = false;
|
||||||
|
if (datepickerPopupConfig.html5Types[attrs.type]) {
|
||||||
|
dateFormat = datepickerPopupConfig.html5Types[attrs.type];
|
||||||
|
isHtml5DateInput = true;
|
||||||
|
} else {
|
||||||
|
dateFormat = attrs.datepickerPopup || datepickerPopupConfig.datepickerPopup;
|
||||||
|
attrs.$observe('datepickerPopup', function(value, oldValue) {
|
||||||
|
var newDateFormat = value || datepickerPopupConfig.datepickerPopup;
|
||||||
|
// Invalidate the $modelValue to ensure that formatters re-run
|
||||||
|
// FIXME: Refactor when PR is merged: https://github.com/angular/angular.js/pull/10764
|
||||||
|
if (newDateFormat !== dateFormat) {
|
||||||
|
dateFormat = newDateFormat;
|
||||||
|
ngModel.$modelValue = null;
|
||||||
|
|
||||||
|
if (!dateFormat) {
|
||||||
|
throw new Error('datepickerPopup must have a date format specified.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dateFormat) {
|
||||||
|
throw new Error('datepickerPopup must have a date format specified.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isHtml5DateInput && attrs.datepickerPopup) {
|
||||||
|
throw new Error('HTML5 date input types do not support custom formats.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// popup element used to display calendar
|
||||||
|
var popupEl = angular.element('<div datepicker-popup-wrap><div datepicker></div></div>');
|
||||||
|
popupEl.attr({
|
||||||
|
'ng-model': 'date',
|
||||||
|
'ng-change': 'dateSelection(date)',
|
||||||
|
'template-url': datepickerPopupTemplateUrl
|
||||||
|
});
|
||||||
|
|
||||||
|
function cameltoDash(string) {
|
||||||
|
return string.replace(/([A-Z])/g, function($1) { return '-' + $1.toLowerCase(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
// datepicker element
|
||||||
|
var datepickerEl = angular.element(popupEl.children()[0]);
|
||||||
|
datepickerEl.attr('template-url', datepickerTemplateUrl);
|
||||||
|
|
||||||
|
if (isHtml5DateInput) {
|
||||||
|
if (attrs.type === 'month') {
|
||||||
|
datepickerEl.attr('datepicker-mode', '"month"');
|
||||||
|
datepickerEl.attr('min-mode', 'month');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attrs.datepickerOptions) {
|
||||||
|
var options = scope.$parent.$eval(attrs.datepickerOptions);
|
||||||
|
if (options && options.initDate) {
|
||||||
|
scope.initDate = options.initDate;
|
||||||
|
datepickerEl.attr('init-date', 'initDate');
|
||||||
|
delete options.initDate;
|
||||||
|
}
|
||||||
|
angular.forEach(options, function(value, option) {
|
||||||
|
datepickerEl.attr(cameltoDash(option), value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.watchData = {};
|
||||||
|
angular.forEach(['minMode', 'maxMode', 'minDate', 'maxDate', 'datepickerMode', 'initDate', 'shortcutPropagation'], function(key) {
|
||||||
|
if (attrs[key]) {
|
||||||
|
var getAttribute = $parse(attrs[key]);
|
||||||
|
scope.$parent.$watch(getAttribute, function(value) {
|
||||||
|
scope.watchData[key] = value;
|
||||||
|
if (key === 'minDate' || key === 'maxDate') {
|
||||||
|
cache[key] = new Date(value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
datepickerEl.attr(cameltoDash(key), 'watchData.' + key);
|
||||||
|
|
||||||
|
// Propagate changes from datepicker to outside
|
||||||
|
if (key === 'datepickerMode') {
|
||||||
|
var setAttribute = getAttribute.assign;
|
||||||
|
scope.$watch('watchData.' + key, function(value, oldvalue) {
|
||||||
|
if (angular.isFunction(setAttribute) && value !== oldvalue) {
|
||||||
|
setAttribute(scope.$parent, value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (attrs.dateDisabled) {
|
||||||
|
datepickerEl.attr('date-disabled', 'dateDisabled({ date: date, mode: mode })');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attrs.showWeeks) {
|
||||||
|
datepickerEl.attr('show-weeks', attrs.showWeeks);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attrs.customClass) {
|
||||||
|
datepickerEl.attr('custom-class', 'customClass({ date: date, mode: mode })');
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseDate(viewValue) {
|
||||||
|
if (angular.isNumber(viewValue)) {
|
||||||
|
// presumably timestamp to date object
|
||||||
|
viewValue = new Date(viewValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!viewValue) {
|
||||||
|
return null;
|
||||||
|
} else if (angular.isDate(viewValue) && !isNaN(viewValue)) {
|
||||||
|
return viewValue;
|
||||||
|
} else if (angular.isString(viewValue)) {
|
||||||
|
var date = dateParser.parse(viewValue, dateFormat, scope.date);
|
||||||
|
if (isNaN(date)) {
|
||||||
|
return undefined;
|
||||||
|
} else {
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function validator(modelValue, viewValue) {
|
||||||
|
var value = modelValue || viewValue;
|
||||||
|
|
||||||
|
if (!attrs.ngRequired && !value) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (angular.isNumber(value)) {
|
||||||
|
value = new Date(value);
|
||||||
|
}
|
||||||
|
if (!value) {
|
||||||
|
return true;
|
||||||
|
} else if (angular.isDate(value) && !isNaN(value)) {
|
||||||
|
return true;
|
||||||
|
} else if (angular.isString(value)) {
|
||||||
|
var date = dateParser.parse(value, dateFormat);
|
||||||
|
return !isNaN(date);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isHtml5DateInput) {
|
||||||
|
// Internal API to maintain the correct ng-invalid-[key] class
|
||||||
|
ngModel.$$parserName = 'date';
|
||||||
|
ngModel.$validators.date = validator;
|
||||||
|
ngModel.$parsers.unshift(parseDate);
|
||||||
|
ngModel.$formatters.push(function(value) {
|
||||||
|
scope.date = value;
|
||||||
|
return ngModel.$isEmpty(value) ? value : dateFilter(value, dateFormat);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
ngModel.$formatters.push(function(value) {
|
||||||
|
scope.date = value;
|
||||||
|
return value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inner change
|
||||||
|
scope.dateSelection = function(dt) {
|
||||||
|
if (angular.isDefined(dt)) {
|
||||||
|
scope.date = dt;
|
||||||
|
}
|
||||||
|
var date = scope.date ? dateFilter(scope.date, dateFormat) : null; // Setting to NULL is necessary for form validators to function
|
||||||
|
element.val(date);
|
||||||
|
ngModel.$setViewValue(date);
|
||||||
|
|
||||||
|
if (closeOnDateSelection) {
|
||||||
|
scope.isOpen = false;
|
||||||
|
element[0].focus();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Detect changes in the view from the text box
|
||||||
|
ngModel.$viewChangeListeners.push(function() {
|
||||||
|
scope.date = dateParser.parse(ngModel.$viewValue, dateFormat, scope.date);
|
||||||
|
});
|
||||||
|
|
||||||
|
var documentClickBind = function(event) {
|
||||||
|
var popup = $popup[0];
|
||||||
|
var dpContainsTarget = element[0].contains(event.target);
|
||||||
|
// The popup node may not be an element node
|
||||||
|
// In some browsers (IE) only element nodes have the 'contains' function
|
||||||
|
var popupContainsTarget = popup.contains !== undefined && popup.contains(event.target);
|
||||||
|
if (scope.isOpen && !(dpContainsTarget || popupContainsTarget)) {
|
||||||
|
scope.$apply(function() {
|
||||||
|
scope.isOpen = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var inputKeydownBind = function(evt) {
|
||||||
|
if (evt.which === 27 && scope.isOpen) {
|
||||||
|
evt.preventDefault();
|
||||||
|
evt.stopPropagation();
|
||||||
|
scope.$apply(function() {
|
||||||
|
scope.isOpen = false;
|
||||||
|
});
|
||||||
|
element[0].focus();
|
||||||
|
} else if (evt.which === 40 && !scope.isOpen) {
|
||||||
|
evt.preventDefault();
|
||||||
|
evt.stopPropagation();
|
||||||
|
scope.$apply(function() {
|
||||||
|
scope.isOpen = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
element.bind('keydown', inputKeydownBind);
|
||||||
|
|
||||||
|
scope.keydown = function(evt) {
|
||||||
|
if (evt.which === 27) {
|
||||||
|
scope.isOpen = false;
|
||||||
|
element[0].focus();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
scope.$watch('isOpen', function(value) {
|
||||||
|
if (value) {
|
||||||
|
scope.position = appendToBody ? $position.offset(element) : $position.position(element);
|
||||||
|
scope.position.top = scope.position.top + element.prop('offsetHeight');
|
||||||
|
|
||||||
|
$timeout(function() {
|
||||||
|
if (onOpenFocus) {
|
||||||
|
scope.$broadcast('datepicker.focus');
|
||||||
|
}
|
||||||
|
$document.bind('click', documentClickBind);
|
||||||
|
}, 0, false);
|
||||||
|
} else {
|
||||||
|
$document.unbind('click', documentClickBind);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
scope.select = function(date) {
|
||||||
|
if (date === 'today') {
|
||||||
|
var today = new Date();
|
||||||
|
if (angular.isDate(scope.date)) {
|
||||||
|
date = new Date(scope.date);
|
||||||
|
date.setFullYear(today.getFullYear(), today.getMonth(), today.getDate());
|
||||||
|
} else {
|
||||||
|
date = new Date(today.setHours(0, 0, 0, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scope.dateSelection(date);
|
||||||
|
};
|
||||||
|
|
||||||
|
scope.close = function() {
|
||||||
|
scope.isOpen = false;
|
||||||
|
element[0].focus();
|
||||||
|
};
|
||||||
|
|
||||||
|
var $popup = $compile(popupEl)(scope);
|
||||||
|
// Prevent jQuery cache memory leak (template is now redundant after linking)
|
||||||
|
popupEl.remove();
|
||||||
|
|
||||||
|
if (appendToBody) {
|
||||||
|
$document.find('body').append($popup);
|
||||||
|
} else {
|
||||||
|
element.after($popup);
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.$on('$destroy', function() {
|
||||||
|
if (scope.isOpen === true) {
|
||||||
|
if (!$rootScope.$$phase) {
|
||||||
|
scope.$apply(function() {
|
||||||
|
scope.isOpen = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$popup.remove();
|
||||||
|
element.unbind('keydown', inputKeydownBind);
|
||||||
|
$document.unbind('click', documentClickBind);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}])
|
||||||
|
|
||||||
|
.directive('datepickerPopupWrap', function() {
|
||||||
|
return {
|
||||||
|
restrict:'EA',
|
||||||
|
replace: true,
|
||||||
|
transclude: true,
|
||||||
|
templateUrl: function(element, attrs) {
|
||||||
|
return attrs.templateUrl || 'template/datepicker/popup.html';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
150
public/vendor/angular-ui/position.js
vendored
Normal file
150
public/vendor/angular-ui/position.js
vendored
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
angular.module('ui.bootstrap.position', [])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A set of utility methods that can be use to retrieve position of DOM elements.
|
||||||
|
* It is meant to be used where we need to absolute-position DOM elements in
|
||||||
|
* relation to other, existing elements (this is the case for tooltips, popovers,
|
||||||
|
* typeahead suggestions etc.).
|
||||||
|
*/
|
||||||
|
.factory('$position', ['$document', '$window', function($document, $window) {
|
||||||
|
function getStyle(el, cssprop) {
|
||||||
|
if (el.currentStyle) { //IE
|
||||||
|
return el.currentStyle[cssprop];
|
||||||
|
} else if ($window.getComputedStyle) {
|
||||||
|
return $window.getComputedStyle(el)[cssprop];
|
||||||
|
}
|
||||||
|
// finally try and get inline style
|
||||||
|
return el.style[cssprop];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a given element is statically positioned
|
||||||
|
* @param element - raw DOM element
|
||||||
|
*/
|
||||||
|
function isStaticPositioned(element) {
|
||||||
|
return (getStyle(element, 'position') || 'static' ) === 'static';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns the closest, non-statically positioned parentOffset of a given element
|
||||||
|
* @param element
|
||||||
|
*/
|
||||||
|
var parentOffsetEl = function(element) {
|
||||||
|
var docDomEl = $document[0];
|
||||||
|
var offsetParent = element.offsetParent || docDomEl;
|
||||||
|
while (offsetParent && offsetParent !== docDomEl && isStaticPositioned(offsetParent) ) {
|
||||||
|
offsetParent = offsetParent.offsetParent;
|
||||||
|
}
|
||||||
|
return offsetParent || docDomEl;
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
/**
|
||||||
|
* Provides read-only equivalent of jQuery's position function:
|
||||||
|
* http://api.jquery.com/position/
|
||||||
|
*/
|
||||||
|
position: function(element) {
|
||||||
|
var elBCR = this.offset(element);
|
||||||
|
var offsetParentBCR = { top: 0, left: 0 };
|
||||||
|
var offsetParentEl = parentOffsetEl(element[0]);
|
||||||
|
if (offsetParentEl != $document[0]) {
|
||||||
|
offsetParentBCR = this.offset(angular.element(offsetParentEl));
|
||||||
|
offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop;
|
||||||
|
offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
var boundingClientRect = element[0].getBoundingClientRect();
|
||||||
|
return {
|
||||||
|
width: boundingClientRect.width || element.prop('offsetWidth'),
|
||||||
|
height: boundingClientRect.height || element.prop('offsetHeight'),
|
||||||
|
top: elBCR.top - offsetParentBCR.top,
|
||||||
|
left: elBCR.left - offsetParentBCR.left
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides read-only equivalent of jQuery's offset function:
|
||||||
|
* http://api.jquery.com/offset/
|
||||||
|
*/
|
||||||
|
offset: function(element) {
|
||||||
|
var boundingClientRect = element[0].getBoundingClientRect();
|
||||||
|
return {
|
||||||
|
width: boundingClientRect.width || element.prop('offsetWidth'),
|
||||||
|
height: boundingClientRect.height || element.prop('offsetHeight'),
|
||||||
|
top: boundingClientRect.top + ($window.pageYOffset || $document[0].documentElement.scrollTop),
|
||||||
|
left: boundingClientRect.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft)
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides coordinates for the targetEl in relation to hostEl
|
||||||
|
*/
|
||||||
|
positionElements: function(hostEl, targetEl, positionStr, appendToBody) {
|
||||||
|
var positionStrParts = positionStr.split('-');
|
||||||
|
var pos0 = positionStrParts[0], pos1 = positionStrParts[1] || 'center';
|
||||||
|
|
||||||
|
var hostElPos,
|
||||||
|
targetElWidth,
|
||||||
|
targetElHeight,
|
||||||
|
targetElPos;
|
||||||
|
|
||||||
|
hostElPos = appendToBody ? this.offset(hostEl) : this.position(hostEl);
|
||||||
|
|
||||||
|
targetElWidth = targetEl.prop('offsetWidth');
|
||||||
|
targetElHeight = targetEl.prop('offsetHeight');
|
||||||
|
|
||||||
|
var shiftWidth = {
|
||||||
|
center: function() {
|
||||||
|
return hostElPos.left + hostElPos.width / 2 - targetElWidth / 2;
|
||||||
|
},
|
||||||
|
left: function() {
|
||||||
|
return hostElPos.left;
|
||||||
|
},
|
||||||
|
right: function() {
|
||||||
|
return hostElPos.left + hostElPos.width;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var shiftHeight = {
|
||||||
|
center: function() {
|
||||||
|
return hostElPos.top + hostElPos.height / 2 - targetElHeight / 2;
|
||||||
|
},
|
||||||
|
top: function() {
|
||||||
|
return hostElPos.top;
|
||||||
|
},
|
||||||
|
bottom: function() {
|
||||||
|
return hostElPos.top + hostElPos.height;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (pos0) {
|
||||||
|
case 'right':
|
||||||
|
targetElPos = {
|
||||||
|
top: shiftHeight[pos1](),
|
||||||
|
left: shiftWidth[pos0]()
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case 'left':
|
||||||
|
targetElPos = {
|
||||||
|
top: shiftHeight[pos1](),
|
||||||
|
left: hostElPos.left - targetElWidth
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case 'bottom':
|
||||||
|
targetElPos = {
|
||||||
|
top: shiftHeight[pos0](),
|
||||||
|
left: shiftWidth[pos1]()
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
targetElPos = {
|
||||||
|
top: hostElPos.top - targetElHeight,
|
||||||
|
left: shiftWidth[pos1]()
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return targetElPos;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}]);
|
1592
public/vendor/angular-ui/ui-bootstrap-tpls.js
vendored
Normal file
1592
public/vendor/angular-ui/ui-bootstrap-tpls.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1476
public/vendor/angular-ui/ui-bootstrap.js
vendored
Normal file
1476
public/vendor/angular-ui/ui-bootstrap.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
public/vendor/css/font-awesome.min.css
vendored
1
public/vendor/css/font-awesome.min.css
vendored
@ -20,6 +20,7 @@
|
|||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* makes the font 33% larger relative to the icon container */
|
/* makes the font 33% larger relative to the icon container */
|
||||||
.fa-lg {
|
.fa-lg {
|
||||||
font-size: 1.33333333em;
|
font-size: 1.33333333em;
|
||||||
|
Loading…
Reference in New Issue
Block a user