Merge pull request #835 from laf/issue-711

Web UI for scheduling maintenance windows
This commit is contained in:
Daniel Preussker
2015-04-21 10:03:00 +00:00
32 changed files with 4210 additions and 1669 deletions
+3 -2
View File
@@ -31,9 +31,9 @@ if (isset($_REQUEST['search']))
if( $_REQUEST['type'] == 'group' ) {
include_once('../includes/device-groups.inc.php');
foreach( dbFetchRows("SELECT name FROM device_groups WHERE name LIKE '%".$search."%'") as $group ) {
foreach( dbFetchRows("SELECT id,name FROM device_groups WHERE name LIKE '%".$search."%'") as $group ) {
if( $_REQUEST['map'] ) {
$results[] = array('name'=>'g:'.$group['name']);
$results[] = array('name'=>'g:'.$group['name'],'group_id'=>$group['id']);
} else {
$results[] = array('name'=>$group['name']);
}
@@ -74,6 +74,7 @@ if (isset($_REQUEST['search']))
}
$num_ports = dbFetchCell("SELECT COUNT(*) FROM `ports` WHERE device_id = ?", array($result['device_id']));
$device[]=array('name'=>$name,
'device_id'=>$result['device_id'],
'url'=> generate_device_url($result),
'colours'=>$highlight_colour,
'device_ports'=>$num_ports,
+109
View File
@@ -0,0 +1,109 @@
<?php
/*
* LibreNMS
*
* Copyright (c) 2014 Neil Lathwood <https://github.com/laf/ http://www.lathwood.co.uk/fa>
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version. Please see LICENSE.txt at the top level of
* the source code distribution for details.
*/
if(is_admin() === false) {
die('ERROR: You need to be admin');
}
$sub_type = $_POST['sub_type'];
if ($sub_type == 'new-maintenance') {
// Defaults
$status = 'error';
$update = 0;
$schedule_id = mres($_POST['schedule_id']);
if ($schedule_id > 0) {
$update = 1;
}
$title = mres($_POST['title']);
$notes = mres($_POST['notes']);
$start = mres($_POST['start']);
$end = mres($_POST['end']);
$maps = mres($_POST['maps']);
if (empty($title)) {
$message = "Missing title<br />";
}
if (empty($start)) {
$message .= "Missing start date<br />";
}
if (empty($end)) {
$message .= "Missing end date<br />";
}
if( !is_array($_POST['maps']) ) {
$message .= "Not mapped to any groups or devices<br />";
}
if (empty($message)) {
if (empty($schedule_id)) {
$schedule_id = dbInsert(array('start'=>$start,'end'=>$end,'title'=>$title,'notes'=>$notes),'alert_schedule');
} else {
dbUpdate(array('start'=>$start,'end'=>$end,'title'=>$title,'notes'=>$notes),'alert_schedule','`schedule_id`=?',array($schedule_id));
}
if ($schedule_id > 0) {
$items = array();
$fail = 0;
if ($update == 1) {
dbDelete('alert_schedule_items', '`schedule_id`=?', array($schedule_id));
}
foreach( $_POST['maps'] as $target ) {
$target = target_to_id($target);
$item = dbInsert(array('schedule_id'=>$schedule_id,'target'=>$target),'alert_schedule_items');
if ($item > 0) {
array_push($items,$item);
} else {
$fail = 1;
}
}
if ($fail == 1 && $update == 0) {
foreach ($items as $item) {
dbDelete('alert_schedule_items', '`item_id`=?', array($item));
}
dbDelete('alert_schedule', '`schedule_id`=?', array($schedule_id));
$message = 'Issue scheduling maintenance';
} else {
$status = 'ok';
$message = 'Scheduling maintenance ok';
}
} else {
$message = "Issue scheduling maintenance";
}
}
$response = array('status'=>$status,'message'=>$message);
} elseif ($sub_type == 'parse-maintenance') {
$schedule_id = mres($_POST['schedule_id']);
$schedule = dbFetchRow("SELECT * FROM `alert_schedule` WHERE `schedule_id`=?",array($schedule_id));
$items = array();
foreach (dbFetchRows("SELECT `target` FROM `alert_schedule_items` WHERE `schedule_id`=?",array($schedule_id)) as $targets) {
$targets = id_to_target($targets['target']);
array_push($items,$targets);
}
$response = array('start'=>$schedule['start'],'end'=>$schedule['end'],'title'=>$schedule['title'],'notes'=>$schedule['notes'],'targets'=>$items);
} elseif ($sub_type == 'del-maintenance') {
$schedule_id = mres($_POST['del_schedule_id']);
dbDelete('alert_schedule_items','`schedule_id`=?',array($schedule_id));
dbDelete('alert_schedule','`schedule_id`=?', array($schedule_id));
$status = 'ok';
$message = 'Maintenance schedule has been removed';
$response = array('status'=>$status,'message'=>$message);
}
echo _json_encode($response);
+187
View File
@@ -0,0 +1,187 @@
<?php
/*
* LibreNMS
*
* Copyright (c) 2014 Neil Lathwood <https://github.com/laf/ http://www.lathwood.co.uk/fa>
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version. Please see LICENSE.txt at the top level of
* the source code distribution for details.
*/
if(is_admin() !== false) {
?>
<div class="modal fade bs-example-modal-sm" id="schedule-maintenance" tabindex="-1" role="dialog" aria-labelledby="Create" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h5 class="modal-title" id="Create">Create maintenance</h5>
</div>
<div class="modal-body">
<form method="post" role="form" id="sched-form" class="form-horizontal schedule-maintenance-form">
<input type="hidden" name="schedule_id" id="schedule_id">
<input type="hidden" name="type" id="type" value="schedule-maintenance">
<input type="hidden" name="sub_type" id="sub_type" value="new-maintenance">
<div class="row">
<div class="col-md-12">
<span id="response"></span>
</div>
</div>
<div class="form-group">
<label for="title" class="col-sm-4 control-label">Title: </label>
<div class="col-sm-8">
<input type="text" class="form-control" id="title" name="title" placeholder="Maintenance title">
</div>
</div>
<div class="form-group">
<label for="notes" class="col-sm-4 control-label">Notes: </label>
<div class="col-sm-8">
<textarea class="form-control" id="notes" name="notes" placeholder="Maintenance notes"></textarea>
</div>
</div>
<div class="form-group">
<label for="start" class="col-sm-4 control-label">Start: </label>
<div class="col-sm-8">
<input type="text" class="form-control date" id="start" name="start" value="<?php echo date('Y-m-d H:i'); ?>" data-date-format="YYYY-MM-DD HH:mm">
</div>
</div>
<div class="form-group">
<label for="end" class="col-sm-4 control-label">End: </label>
<div class="col-sm-8">
<input type="text" class="form-control date" id="end" name="end" value="<?php echo date('Y-m-d H:i',strtotime('+1 hour')); ?>" data-date-format="YYYY-MM-DD HH:mm">
</div>
</div>
<div class="form-group">
<label for='map-stub' class='col-sm-4 control-label'>Map To: </label>
<div class="col-sm-5">
<input type='text' id='map-stub' name='map-stub' class='form-control'/>
</div>
<div class="col-sm-3">
<button class="btn btn-primary btn-sm" type="button" name="add-map" id="add-map" value="Add">Add</button>
</div>
</div>
<div class="form-group">
<div class="col-md-12">
<span id="map-tags"></span>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-3">
<button class="btn btn-success" type="submit" name="sched-submit" id="sched-submit" value="save">Schedule maintenance</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<script>
$('#schedule-maintenance').on('hide.bs.modal', function (event) {
$('#map-tags').data('tagmanager').empty();
$('#schedule_id').val('');
$('#title').val('');
$('#notes').val('');
$('#start').val('');
$('#end').val('');
});
$('#schedule-maintenance').on('show.bs.modal', function (event) {
$('#tagmanager').tagmanager();
var schedule_id = $('#schedule_id').val();
$('#map-tags').tagmanager({
strategy: 'array',
tagFieldName: 'maps[]',
initialCap: false
});
if (schedule_id > 0) {
$.ajax({
type: "POST",
url: "/ajax_form.php",
data: { type: "schedule-maintenance", sub_type: "parse-maintenance", schedule_id: schedule_id },
dataType: "json",
success: function(output) {
var arr = [];
$.each ( output['targets'], function( key, value ) {
arr.push(value);
});
$('#map-tags').data('tagmanager').populate(arr);
$('#title').val(output['title']);
$('#notes').val(output['notes']);
$('#start').val(output['start']);
$('#end').val(output['end']);
}
});
}
});
$('#sched-submit').click('', function(e) {
e.preventDefault();
$.ajax({
type: "POST",
url: "/ajax_form.php",
data: $('form.schedule-maintenance-form').serialize(),
dataType: "json",
success: function(data){
if(data.status == 'ok') {
$("#message").html('<div class="alert alert-info">'+data.message+'</div>');
$("#schedule-maintenance").modal('hide');
$("#alert-schedule").bootgrid('reload');
} else {
$("#response").html('<div class="alert alert-info">'+data.message+'</div>');
}
},
error: function(){
$("#response").html('<div class="alert alert-info">An error occurred.</div>');
}
});
});
$('#add-map').click('',function (event) {
$('#map-tags').data('tagmanager').populate([ $('#map-stub').val() ]);
$('#map-stub').val('');
});
$('#map-stub').typeahead([
{
name: 'map_devices',
remote : '/ajax_search.php?search=%QUERY&type=device&map=1',
header : '<h5><strong>&nbsp;Devices</strong></h5>',
template: '{{name}}',
valueKey:"name",
engine: Hogan
},
{
name: 'map_groups',
remote : '/ajax_search.php?search=%QUERY&type=group&map=1',
header : '<h5><strong>&nbsp;Groups</strong></h5>',
template: '{{name}}',
valueKey:"name",
engine: Hogan
}
]);
$(function () {
$("#start").datetimepicker({
minDate: '<?php echo date('Y-m-d H:i'); ?>'
});
$("#end").datetimepicker();
$("#start").on("dp.change", function (e) {
$("#end").data("DateTimePicker").minDate(e.date);
});
$("#end").on("dp.change", function (e) {
$("#start").data("DateTimePicker").maxDate(e.date);
});
});
</script>
<?php
}
?>
@@ -0,0 +1,78 @@
<?php
/*
* LibreNMS
*
* Copyright (c) 2014 Neil Lathwood <https://github.com/laf/ http://www.lathwood.co.uk/fa>
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version. Please see LICENSE.txt at the top level of
* the source code distribution for details.
*/
if(is_admin() !== false) {
?>
<div class="modal fade bs-example-modal-sm" id="delete-maintenance" tabindex="-1" role="dialog" aria-labelledby="delete" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h5 class="modal-title" id="Create">Delete maintenance</h5>
</div>
<div class="modal-body">
<p>If you would like to remove this maintenance then please click Delete.</p>
</div>
<div class="modal-footer">
<form method="post" role="form" id="sched-del" class="form-horizontal schedule-maintenance-del">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-danger danger" id="sched-maintenance-removal" data-target="sched-maintenance-removal">Delete</button>
<input type="hidden" name="del_schedule_id" id="del_schedule_id">
<input type="hidden" name="type" id="type" value="schedule-maintenance">
<input type="hidden" name="sub_type" id="sub_type" value="del-maintenance">
</form>
</div>
</div>
</div>
</div>
<script>
$('#schedule-maintenance').on('hide.bs.modal', function (event) {
$('#map-tags').data('tagmanager').empty();
$('#schedule_id').val('');
$('#title').val('');
$('#notes').val('');
$('#start').val('');
$('#end').val('');
});
$('#sched-maintenance-removal').click('', function(e) {
e.preventDefault();
$.ajax({
type: "POST",
url: "/ajax_form.php",
data: $('form.schedule-maintenance-del').serialize(),
dataType: "json",
success: function(data){
if(data.status == 'ok') {
$("#message").html('<div class="alert alert-info">'+data.message+'</div>');
$("#delete-maintenance").modal('hide');
$("#alert-schedule").bootgrid('reload');
} else {
$("#response").html('<div class="alert alert-info">'+data.message+'</div>');
}
},
error: function(){
$("#response").html('<div class="alert alert-info">An error occurred.</div>');
}
});
});
</script>
<?php
}
?>
+1
View File
@@ -62,6 +62,7 @@ if (isset($config['site_style']) && ($config['site_style'] == 'dark' || $config[
if ($_SESSION['userlevel'] >= '10') {
?>
<li><a href="<?php echo(generate_url(array('page'=>'alert-rules'))); ?>"><i class="fa fa-tasks fa-fw fa-lg"></i> Alert Rules</a></li>
<li><a href="<?php echo(generate_url(array('page'=>'alert-schedule'))); ?>"><i class="fa fa-calendar fa-fw fa-lg"></i> Maintenance Windows</a></li>
<li><a href="<?php echo(generate_url(array('page'=>'alert-map'))); ?>"><i class="fa fa-link fa-fw fa-lg"></i> Alert Map</a></li>
<li><a href="<?php echo(generate_url(array('page'=>'templates'))); ?>"><i class="fa fa-sitemap fa-fw fa-lg"></i> Alert Templates</a></li>
<?php
@@ -0,0 +1,67 @@
<?php
/*
* LibreNMS
*
* Copyright (c) 2014 Neil Lathwood <https://github.com/laf/ http://www.lathwood.co.uk/fa>
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version. Please see LICENSE.txt at the top level of
* the source code distribution for details.
*/
$where = 1;
if ($_SESSION['userlevel'] >= '5') {
$sql = " FROM `alert_schedule` AS S WHERE $where";
} else {
$sql = " FROM `alert_schedule` AS S WHERE $where";
$param[] = $_SESSION['user_id'];
}
if (isset($searchPhrase) && !empty($searchPhrase)) {
$sql .= " AND (`S`.`title` LIKE '%$searchPhrase%' OR `S`.`start` LIKE '%$searchPhrase%' OR `S`.`end` LIKE '%$searchPhrase%')";
}
$count_sql = "SELECT COUNT(`id`) $sql";
$total = dbFetchCell($count_sql,$param);
if (empty($total)) {
$total = 0;
}
if (!isset($sort) || empty($sort)) {
$sort = '`S`.`start` DESC ';
}
$sql .= " ORDER BY $sort";
if (isset($current)) {
$limit_low = ($current * $rowCount) - ($rowCount);
$limit_high = $rowCount;
}
if ($rowCount != -1) {
$sql .= " LIMIT $limit_low,$limit_high";
}
$sql = "SELECT `S`.`schedule_id`, DATE_FORMAT(`S`.`start`, '%D %b %Y %T') AS `start`, DATE_FORMAT(`S`.`end`, '%D %b %Y %T') AS `end`, `S`.`title` $sql";
foreach (dbFetchRows($sql,$param) as $schedule) {
$status = 0;
if ($schedule['end'] < date('dS M Y H:i::s')) {
$status = 1;
}
if (date('dS M Y H:i::s') >= $schedule['start'] && date('dS M Y H:i::s') < $schedule['end']) {
$status = 2;
}
$response[] = array('title'=>$schedule['title'],
'start'=>$schedule['start'],
'end'=>$schedule['end'],
'id'=>$schedule['schedule_id'],
'status'=>$status);
}
$output = array('current'=>$current,'rowCount'=>$rowCount,'rows'=>$response,'total'=>$total);
echo _json_encode($output);
-1
View File
@@ -54,7 +54,6 @@
"requireCamelCaseOrUpperCaseIdentifiers": "ignoreProperties",
"disallowKeywords": ["with"],
"disallowMultipleLineStrings": true,
"validateLineBreaks": "LF",
"validateIndentation": 4,
"disallowTrailingWhitespace": true,
"disallowTrailingComma": true,
+7 -1
View File
@@ -1,7 +1,9 @@
Submitting Issues
=================
If you are submitting a bug, please test and/or fork [this jsfiddle](http://jsfiddle.net/kmbo576p/) demonstrating the issue. Code issues and fringe case bugs that do not include a jsfiddle (or similar) will be closed.
If you are submitting a bug, please test and/or fork [this jsfiddle](http://jsfiddle.net/Eonasdan/0Ltv25o8/) demonstrating the issue. Code issues and fringe case bugs that do not include a jsfiddle (or similar) will be closed.
Issues that are submitted without a description (title only) will be closed with no further explanation.
Contributing code
=================
@@ -22,6 +24,9 @@ Very important notes
* **Pull requests to the `master` branch will be closed.** Please submit all pull requests to the `development` branch.
* **Do not include the minified files in your pull request.** Don't worry, we'll build them when we cut a release.
* Pull requests that do not include a description (title only) and the following will be closed:
* What the change does
* A use case (for new features or enhancements)
Grunt tasks
===========
@@ -30,3 +35,4 @@ We use Grunt for managing the build. Here are some useful Grunt tasks:
* `grunt` The default task lints the code and runs the tests. You should make sure you do this before submitting a PR.
* `grunt build` Compiles the less stylesheet and minifies the javascript source in build directory.
* `grunt build:travis` Compliles and runs the jasmine/travis tests. **All PR's MUST pass tests in place**
+46 -7
View File
@@ -24,11 +24,12 @@ module.exports = function (grunt) {
jshint: {
all: [
'Gruntfile.js', 'src/js/*.js'
'Gruntfile.js', 'src/js/*.js', 'test/*.js'
],
options: {
'browser' : true,
'node' : true,
'jquery' : true,
'boss' : false,
'curly' : true,
'debug' : false,
@@ -57,15 +58,24 @@ module.exports = function (grunt) {
'quotmark' : 'single',
'globals': {
'define': false,
'jQuery': false,
'moment': false
'moment': false,
// Jasmine
'jasmine': false,
'describe': false,
'xdescribe': false,
'expect': false,
'it': false,
'xit': false,
'spyOn': false,
'beforeEach': false,
'afterEach': false
}
}
},
jscs: {
all: [
'Gruntfile.js', 'src/js/*.js'
'Gruntfile.js', 'src/js/*.js', 'test/*.js'
],
options: {
config: '.jscs.json'
@@ -86,22 +96,49 @@ module.exports = function (grunt) {
'build/css/bootstrap-datetimepicker.css': 'src/less/bootstrap-datetimepicker-build.less'
}
}
},
jasmine: {
customTemplate: {
src: 'src/js/*.js',
options: {
specs: 'test/*Spec.js',
helpers: 'test/*Helper.js',
styles: [
'node_modules/bootstrap/dist/css/bootstrap.min.css',
'build/css/bootstrap-datetimepicker.min.css'
],
vendor: [
'node_modules/jquery/dist/jquery.min.js',
'node_modules/moment/min/moment-with-locales.min.js',
'node_modules/bootstrap/dist/js/bootstrap.min.js'
],
display: 'none',
summary: 'true'
}
}
}
});
grunt.loadTasks('tasks');
grunt.loadNpmTasks('grunt-contrib-jasmine');
// These plugins provide necessary tasks.
require('load-grunt-tasks')(grunt);
// Default task.
grunt.registerTask('default', ['jshint', 'jscs']);
grunt.registerTask('default', ['jshint', 'jscs', 'less', 'jasmine']);
// travis build task
grunt.registerTask('build:travis', [
// code style
'jshint', 'jscs'
'jshint', 'jscs',
// build
'uglify', 'less',
// tests
'jasmine'
]);
// Task to be run when building
@@ -131,7 +168,7 @@ module.exports = function (grunt) {
done();
});
}
else {
else { //--target=css
grunt.util.spawn({
cmd: 'src/nuget/nuget.exe',
args: [
@@ -152,4 +189,6 @@ module.exports = function (grunt) {
});
}
});
grunt.registerTask('test', ['jshint', 'jscs', 'uglify', 'less', 'jasmine']);
};
+1 -1
View File
@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2014 Eonasdan, nikoskalogridis
Copyright (c) 2015 Jonathan Peterson (@Eonasdan)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+15 -7
View File
@@ -1,17 +1,25 @@
# Bootstrap v3 datetimepicker widget ![GitHub version](https://badge.fury.io/gh/Eonasdan%2Fbootstrap-datetimepicker.png)&nbsp;&nbsp;&nbsp;![Travis](https://travis-ci.org/Eonasdan/bootstrap-datetimepicker.svg?branch=development)
# Bootstrap 3 Date/Time Picker
![GitHub version](https://badge.fury.io/gh/Eonasdan%2Fbootstrap-datetimepicker.png)&nbsp;&nbsp;&nbsp;![Travis](https://travis-ci.org/Eonasdan/bootstrap-datetimepicker.svg?branch=development)
![DateTimePicker](http://i.imgur.com/nfnvh5g.png)
## [View the manual and demos](http://eonasdan.github.io/bootstrap-datetimepicker/)
#v4
v4 is out now! For v4 related bugs and issues see: /Eonasdan/bootstrap-datetimepicker/labels/v4.
v3 is going into an archive state. Please be sure to check the documents. v4 has breaking changes and is a major rewrite.
## Submitting Issues
Please test and/or fork [this jsfiddle](http://jsfiddle.net/kmbo576p/) with an example of your issue before you post an issue here.
If you have issues, please check the following first:
* Have you read the docs?
* Do you have the latest version of momentjs?
* Do you have the latest version of jQuery?
* Please test and/or fork [this jsfiddle](http://jsfiddle.net/Eonasdan/0Ltv25o8/) with an example of your issue before you post an issue here.
## Where do you use this?
I'd love to know if your public site is using this plugin and list your logo on the documentation site. Please email me `eonasdan at outlook dot com`. Do not submit issue/feature request to this email, they will be ignored.
I'd love to know if your public site is using this plugin and list your logo on the documentation site. Please email me `eonasdan at outlook dot com`. Do not submit issue/feature request to this email, they will be **ignored**.
## [Installation instructions](https://github.com/Eonasdan/bootstrap-datetimepicker/wiki/Installation)
Installation instructions has been moved to the wiki
## [Installation instructions](http://eonasdan.github.io/bootstrap-datetimepicker/Installing/)
## [Change Log](https://github.com/Eonasdan/bootstrap-datetimepicker/wiki/Change-Log)
The change log has moved to the wiki
## [Change Log](http://eonasdan.github.io/bootstrap-datetimepicker/Version%204%20Changelog/)
+5 -3
View File
@@ -1,13 +1,15 @@
{
"name": "eonasdan-bootstrap-datetimepicker",
"version": "3.1.3",
"version": "4.7.14",
"main": [
"build/css/bootstrap-datetimepicker.min.css",
"build/js/bootstrap-datetimepicker.min.js"
"build/js/bootstrap-datetimepicker.min.js",
"src/less/_bootstrap-datetimepicker.less",
"src/less/bootstrap-datetimepicker-build.less",
"src/js/bootstrap-datetimepicker.js"
],
"dependencies": {
"jquery": ">=1.8.3",
"bootstrap": ">= 3.0",
"moment": ">=2.8.0"
},
"homepage": "https://github.com/Eonasdan/bootstrap-datetimepicker",
+245 -117
View File
@@ -1,75 +1,76 @@
/*!
* Datetimepicker for Bootstrap v3
//! version : 3.1.3
* Datetimepicker for Bootstrap 3
* ! version : 4.7.14
* https://github.com/Eonasdan/bootstrap-datetimepicker/
*/
.bootstrap-datetimepicker-widget {
top: 0;
left: 0;
width: 250px;
list-style: none;
}
.bootstrap-datetimepicker-widget.dropdown-menu {
margin: 2px 0;
padding: 4px;
margin-top: 1px;
z-index: 99999 !important;
border-radius: 4px;
width: 19em;
}
.bootstrap-datetimepicker-widget.timepicker-sbs {
width: 600px;
@media (min-width: 768px) {
.bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs {
width: 38em;
}
}
.bootstrap-datetimepicker-widget.bottom:before {
@media (min-width: 992px) {
.bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs {
width: 38em;
}
}
@media (min-width: 1200px) {
.bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs {
width: 38em;
}
}
.bootstrap-datetimepicker-widget.dropdown-menu:before,
.bootstrap-datetimepicker-widget.dropdown-menu:after {
content: '';
display: inline-block;
position: absolute;
}
.bootstrap-datetimepicker-widget.dropdown-menu.bottom:before {
border-left: 7px solid transparent;
border-right: 7px solid transparent;
border-bottom: 7px solid #ccc;
border-bottom: 7px solid #cccccc;
border-bottom-color: rgba(0, 0, 0, 0.2);
position: absolute;
top: -7px;
left: 7px;
}
.bootstrap-datetimepicker-widget.bottom:after {
content: '';
display: inline-block;
.bootstrap-datetimepicker-widget.dropdown-menu.bottom:after {
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-bottom: 6px solid white;
position: absolute;
top: -6px;
left: 8px;
}
.bootstrap-datetimepicker-widget.top:before {
content: '';
display: inline-block;
.bootstrap-datetimepicker-widget.dropdown-menu.top:before {
border-left: 7px solid transparent;
border-right: 7px solid transparent;
border-top: 7px solid #ccc;
border-top: 7px solid #cccccc;
border-top-color: rgba(0, 0, 0, 0.2);
position: absolute;
bottom: -7px;
left: 6px;
}
.bootstrap-datetimepicker-widget.top:after {
content: '';
display: inline-block;
.bootstrap-datetimepicker-widget.dropdown-menu.top:after {
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-top: 6px solid white;
position: absolute;
bottom: -6px;
left: 7px;
}
.bootstrap-datetimepicker-widget .dow {
width: 14.2857%;
}
.bootstrap-datetimepicker-widget.pull-right:before {
.bootstrap-datetimepicker-widget.dropdown-menu.pull-right:before {
left: auto;
right: 6px;
}
.bootstrap-datetimepicker-widget.pull-right:after {
.bootstrap-datetimepicker-widget.dropdown-menu.pull-right:after {
left: auto;
right: 7px;
}
.bootstrap-datetimepicker-widget > ul {
list-style-type: none;
.bootstrap-datetimepicker-widget .list-unstyled {
margin: 0;
}
.bootstrap-datetimepicker-widget a[data-action] {
@@ -89,82 +90,238 @@
.bootstrap-datetimepicker-widget button[data-action] {
padding: 6px;
}
.bootstrap-datetimepicker-widget table[data-hour-format="12"] .separator {
width: 4px;
.bootstrap-datetimepicker-widget .btn[data-action="incrementHours"]::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
margin: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Increment Hours";
}
.bootstrap-datetimepicker-widget .datepicker > div {
display: none;
.bootstrap-datetimepicker-widget .btn[data-action="incrementMinutes"]::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Increment Minutes";
}
.bootstrap-datetimepicker-widget .btn[data-action="decrementHours"]::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Decrement Hours";
}
.bootstrap-datetimepicker-widget .btn[data-action="decrementMinutes"]::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Decrement Minutes";
}
.bootstrap-datetimepicker-widget .btn[data-action="showHours"]::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Show Hours";
}
.bootstrap-datetimepicker-widget .btn[data-action="showMinutes"]::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Show Minutes";
}
.bootstrap-datetimepicker-widget .btn[data-action="togglePeriod"]::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Toggle AM/PM";
}
.bootstrap-datetimepicker-widget .btn[data-action="clear"]::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Clear the picker";
}
.bootstrap-datetimepicker-widget .btn[data-action="today"]::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Set the date to today";
}
.bootstrap-datetimepicker-widget .picker-switch {
text-align: center;
}
.bootstrap-datetimepicker-widget .picker-switch::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Toggle Date and Time Screens";
}
.bootstrap-datetimepicker-widget .picker-switch td {
padding: 0;
margin: 0;
height: auto;
width: auto;
line-height: inherit;
}
.bootstrap-datetimepicker-widget .picker-switch td span {
line-height: 2.5;
height: 2.5em;
width: 100%;
}
.bootstrap-datetimepicker-widget table {
width: 100%;
margin: 0;
}
.bootstrap-datetimepicker-widget td,
.bootstrap-datetimepicker-widget th {
.bootstrap-datetimepicker-widget table td,
.bootstrap-datetimepicker-widget table th {
text-align: center;
border-radius: 4px;
}
.bootstrap-datetimepicker-widget td {
height: 54px;
line-height: 54px;
width: 54px;
}
.bootstrap-datetimepicker-widget td.cw {
font-size: 10px;
height: 20px;
line-height: 20px;
color: #777777;
}
.bootstrap-datetimepicker-widget td.day {
.bootstrap-datetimepicker-widget table th {
height: 20px;
line-height: 20px;
width: 20px;
}
.bootstrap-datetimepicker-widget td.day:hover,
.bootstrap-datetimepicker-widget td.hour:hover,
.bootstrap-datetimepicker-widget td.minute:hover,
.bootstrap-datetimepicker-widget td.second:hover {
.bootstrap-datetimepicker-widget table th.picker-switch {
width: 145px;
}
.bootstrap-datetimepicker-widget table th.disabled,
.bootstrap-datetimepicker-widget table th.disabled:hover {
background: none;
color: #777777;
cursor: not-allowed;
}
.bootstrap-datetimepicker-widget table th.prev::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Previous Month";
}
.bootstrap-datetimepicker-widget table th.next::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Next Month";
}
.bootstrap-datetimepicker-widget table thead tr:first-child th {
cursor: pointer;
}
.bootstrap-datetimepicker-widget table thead tr:first-child th:hover {
background: #eeeeee;
}
.bootstrap-datetimepicker-widget table td {
height: 54px;
line-height: 54px;
width: 54px;
}
.bootstrap-datetimepicker-widget table td.cw {
font-size: .8em;
height: 20px;
line-height: 20px;
color: #777777;
}
.bootstrap-datetimepicker-widget table td.day {
height: 20px;
line-height: 20px;
width: 20px;
}
.bootstrap-datetimepicker-widget table td.day:hover,
.bootstrap-datetimepicker-widget table td.hour:hover,
.bootstrap-datetimepicker-widget table td.minute:hover,
.bootstrap-datetimepicker-widget table td.second:hover {
background: #eeeeee;
cursor: pointer;
}
.bootstrap-datetimepicker-widget td.old,
.bootstrap-datetimepicker-widget td.new {
.bootstrap-datetimepicker-widget table td.old,
.bootstrap-datetimepicker-widget table td.new {
color: #777777;
}
.bootstrap-datetimepicker-widget td.today {
.bootstrap-datetimepicker-widget table td.today {
position: relative;
}
.bootstrap-datetimepicker-widget td.today:before {
.bootstrap-datetimepicker-widget table td.today:before {
content: '';
display: inline-block;
border-left: 7px solid transparent;
border-bottom: 7px solid #428bca;
border: 0 0 7px 7px solid transparent;
border-bottom-color: #337ab7;
border-top-color: rgba(0, 0, 0, 0.2);
position: absolute;
bottom: 4px;
right: 4px;
}
.bootstrap-datetimepicker-widget td.active,
.bootstrap-datetimepicker-widget td.active:hover {
background-color: #428bca;
.bootstrap-datetimepicker-widget table td.active,
.bootstrap-datetimepicker-widget table td.active:hover {
background-color: #337ab7;
color: #ffffff;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.bootstrap-datetimepicker-widget td.active.today:before {
.bootstrap-datetimepicker-widget table td.active.today:before {
border-bottom-color: #fff;
}
.bootstrap-datetimepicker-widget td.disabled,
.bootstrap-datetimepicker-widget td.disabled:hover {
.bootstrap-datetimepicker-widget table td.disabled,
.bootstrap-datetimepicker-widget table td.disabled:hover {
background: none;
color: #777777;
cursor: not-allowed;
}
.bootstrap-datetimepicker-widget td span {
.bootstrap-datetimepicker-widget table td span {
display: inline-block;
width: 54px;
height: 54px;
@@ -173,66 +330,37 @@
cursor: pointer;
border-radius: 4px;
}
.bootstrap-datetimepicker-widget td span:hover {
.bootstrap-datetimepicker-widget table td span:hover {
background: #eeeeee;
}
.bootstrap-datetimepicker-widget td span.active {
background-color: #428bca;
.bootstrap-datetimepicker-widget table td span.active {
background-color: #337ab7;
color: #ffffff;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.bootstrap-datetimepicker-widget td span.old {
.bootstrap-datetimepicker-widget table td span.old {
color: #777777;
}
.bootstrap-datetimepicker-widget td span.disabled,
.bootstrap-datetimepicker-widget td span.disabled:hover {
.bootstrap-datetimepicker-widget table td span.disabled,
.bootstrap-datetimepicker-widget table td span.disabled:hover {
background: none;
color: #777777;
cursor: not-allowed;
}
.bootstrap-datetimepicker-widget th {
height: 20px;
line-height: 20px;
width: 20px;
.bootstrap-datetimepicker-widget.usetwentyfour td.hour {
height: 27px;
line-height: 27px;
}
.bootstrap-datetimepicker-widget th.picker-switch {
width: 145px;
}
.bootstrap-datetimepicker-widget th.next,
.bootstrap-datetimepicker-widget th.prev {
font-size: 21px;
}
.bootstrap-datetimepicker-widget th.disabled,
.bootstrap-datetimepicker-widget th.disabled:hover {
background: none;
color: #777777;
cursor: not-allowed;
}
.bootstrap-datetimepicker-widget thead tr:first-child th {
.input-group.date .input-group-addon {
cursor: pointer;
}
.bootstrap-datetimepicker-widget thead tr:first-child th:hover {
background: #eeeeee;
}
.input-group.date .input-group-addon span {
display: block;
cursor: pointer;
width: 16px;
height: 16px;
}
.bootstrap-datetimepicker-widget.left-oriented:before {
left: auto;
right: 6px;
}
.bootstrap-datetimepicker-widget.left-oriented:after {
left: auto;
right: 7px;
}
.bootstrap-datetimepicker-widget ul.list-unstyled li div.timepicker div.timepicker-picker table.table-condensed tbody > tr > td {
padding: 0px !important;
}
@media screen and (max-width: 767px) {
.bootstrap-datetimepicker-widget.timepicker-sbs {
width: 283px;
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
}
+364 -3
View File
@@ -1,5 +1,366 @@
/*!
* Datetimepicker for Bootstrap v3
//! version : 3.1.3
* Datetimepicker for Bootstrap 3
* ! version : 4.7.14
* https://github.com/Eonasdan/bootstrap-datetimepicker/
*/.bootstrap-datetimepicker-widget{top:0;left:0;width:250px;padding:4px;margin-top:1px;z-index:99999!important;border-radius:4px}.bootstrap-datetimepicker-widget.timepicker-sbs{width:600px}.bootstrap-datetimepicker-widget.bottom: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,.2);position:absolute;top:-7px;left:7px}.bootstrap-datetimepicker-widget.bottom:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #fff;position:absolute;top:-6px;left:8px}.bootstrap-datetimepicker-widget.top:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-top:7px solid #ccc;border-top-color:rgba(0,0,0,.2);position:absolute;bottom:-7px;left:6px}.bootstrap-datetimepicker-widget.top:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-top:6px solid #fff;position:absolute;bottom:-6px;left:7px}.bootstrap-datetimepicker-widget .dow{width:14.2857%}.bootstrap-datetimepicker-widget.pull-right:before{left:auto;right:6px}.bootstrap-datetimepicker-widget.pull-right:after{left:auto;right:7px}.bootstrap-datetimepicker-widget>ul{list-style-type:none;margin:0}.bootstrap-datetimepicker-widget a[data-action]{padding:6px 0}.bootstrap-datetimepicker-widget a[data-action]:active{box-shadow:none}.bootstrap-datetimepicker-widget .timepicker-hour,.bootstrap-datetimepicker-widget .timepicker-minute,.bootstrap-datetimepicker-widget .timepicker-second{width:54px;font-weight:700;font-size:1.2em;margin:0}.bootstrap-datetimepicker-widget button[data-action]{padding:6px}.bootstrap-datetimepicker-widget table[data-hour-format="12"] .separator{width:4px;padding:0;margin:0}.bootstrap-datetimepicker-widget .datepicker>div{display:none}.bootstrap-datetimepicker-widget .picker-switch{text-align:center}.bootstrap-datetimepicker-widget table{width:100%;margin:0}.bootstrap-datetimepicker-widget td,.bootstrap-datetimepicker-widget th{text-align:center;border-radius:4px}.bootstrap-datetimepicker-widget td{height:54px;line-height:54px;width:54px}.bootstrap-datetimepicker-widget td.cw{font-size:10px;height:20px;line-height:20px;color:#777}.bootstrap-datetimepicker-widget td.day{height:20px;line-height:20px;width:20px}.bootstrap-datetimepicker-widget td.day:hover,.bootstrap-datetimepicker-widget td.hour:hover,.bootstrap-datetimepicker-widget td.minute:hover,.bootstrap-datetimepicker-widget td.second:hover{background:#eee;cursor:pointer}.bootstrap-datetimepicker-widget td.old,.bootstrap-datetimepicker-widget td.new{color:#777}.bootstrap-datetimepicker-widget td.today{position:relative}.bootstrap-datetimepicker-widget td.today:before{content:'';display:inline-block;border-left:7px solid transparent;border-bottom:7px solid #428bca;border-top-color:rgba(0,0,0,.2);position:absolute;bottom:4px;right:4px}.bootstrap-datetimepicker-widget td.active,.bootstrap-datetimepicker-widget td.active:hover{background-color:#428bca;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25)}.bootstrap-datetimepicker-widget td.active.today:before{border-bottom-color:#fff}.bootstrap-datetimepicker-widget td.disabled,.bootstrap-datetimepicker-widget td.disabled:hover{background:0 0;color:#777;cursor:not-allowed}.bootstrap-datetimepicker-widget td span{display:inline-block;width:54px;height:54px;line-height:54px;margin:2px 1.5px;cursor:pointer;border-radius:4px}.bootstrap-datetimepicker-widget td span:hover{background:#eee}.bootstrap-datetimepicker-widget td span.active{background-color:#428bca;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25)}.bootstrap-datetimepicker-widget td span.old{color:#777}.bootstrap-datetimepicker-widget td span.disabled,.bootstrap-datetimepicker-widget td span.disabled:hover{background:0 0;color:#777;cursor:not-allowed}.bootstrap-datetimepicker-widget th{height:20px;line-height:20px;width:20px}.bootstrap-datetimepicker-widget th.picker-switch{width:145px}.bootstrap-datetimepicker-widget th.next,.bootstrap-datetimepicker-widget th.prev{font-size:21px}.bootstrap-datetimepicker-widget th.disabled,.bootstrap-datetimepicker-widget th.disabled:hover{background:0 0;color:#777;cursor:not-allowed}.bootstrap-datetimepicker-widget thead tr:first-child th{cursor:pointer}.bootstrap-datetimepicker-widget thead tr:first-child th:hover{background:#eee}.input-group.date .input-group-addon span{display:block;cursor:pointer;width:16px;height:16px}.bootstrap-datetimepicker-widget.left-oriented:before{left:auto;right:6px}.bootstrap-datetimepicker-widget.left-oriented:after{left:auto;right:7px}.bootstrap-datetimepicker-widget ul.list-unstyled li div.timepicker div.timepicker-picker table.table-condensed tbody>tr>td{padding:0!important}@media screen and (max-width:767px){.bootstrap-datetimepicker-widget.timepicker-sbs{width:283px}}
*/
.bootstrap-datetimepicker-widget {
list-style: none;
}
.bootstrap-datetimepicker-widget.dropdown-menu {
margin: 2px 0;
padding: 4px;
width: 19em;
}
@media (min-width: 768px) {
.bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs {
width: 38em;
}
}
@media (min-width: 992px) {
.bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs {
width: 38em;
}
}
@media (min-width: 1200px) {
.bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs {
width: 38em;
}
}
.bootstrap-datetimepicker-widget.dropdown-menu:before,
.bootstrap-datetimepicker-widget.dropdown-menu:after {
content: '';
display: inline-block;
position: absolute;
}
.bootstrap-datetimepicker-widget.dropdown-menu.bottom:before {
border-left: 7px solid transparent;
border-right: 7px solid transparent;
border-bottom: 7px solid #cccccc;
border-bottom-color: rgba(0, 0, 0, 0.2);
top: -7px;
left: 7px;
}
.bootstrap-datetimepicker-widget.dropdown-menu.bottom:after {
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-bottom: 6px solid white;
top: -6px;
left: 8px;
}
.bootstrap-datetimepicker-widget.dropdown-menu.top:before {
border-left: 7px solid transparent;
border-right: 7px solid transparent;
border-top: 7px solid #cccccc;
border-top-color: rgba(0, 0, 0, 0.2);
bottom: -7px;
left: 6px;
}
.bootstrap-datetimepicker-widget.dropdown-menu.top:after {
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-top: 6px solid white;
bottom: -6px;
left: 7px;
}
.bootstrap-datetimepicker-widget.dropdown-menu.pull-right:before {
left: auto;
right: 6px;
}
.bootstrap-datetimepicker-widget.dropdown-menu.pull-right:after {
left: auto;
right: 7px;
}
.bootstrap-datetimepicker-widget .list-unstyled {
margin: 0;
}
.bootstrap-datetimepicker-widget a[data-action] {
padding: 6px 0;
}
.bootstrap-datetimepicker-widget a[data-action]:active {
box-shadow: none;
}
.bootstrap-datetimepicker-widget .timepicker-hour,
.bootstrap-datetimepicker-widget .timepicker-minute,
.bootstrap-datetimepicker-widget .timepicker-second {
width: 54px;
font-weight: bold;
font-size: 1.2em;
margin: 0;
}
.bootstrap-datetimepicker-widget button[data-action] {
padding: 6px;
}
.bootstrap-datetimepicker-widget .btn[data-action="incrementHours"]::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Increment Hours";
}
.bootstrap-datetimepicker-widget .btn[data-action="incrementMinutes"]::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Increment Minutes";
}
.bootstrap-datetimepicker-widget .btn[data-action="decrementHours"]::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Decrement Hours";
}
.bootstrap-datetimepicker-widget .btn[data-action="decrementMinutes"]::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Decrement Minutes";
}
.bootstrap-datetimepicker-widget .btn[data-action="showHours"]::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Show Hours";
}
.bootstrap-datetimepicker-widget .btn[data-action="showMinutes"]::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Show Minutes";
}
.bootstrap-datetimepicker-widget .btn[data-action="togglePeriod"]::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Toggle AM/PM";
}
.bootstrap-datetimepicker-widget .btn[data-action="clear"]::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Clear the picker";
}
.bootstrap-datetimepicker-widget .btn[data-action="today"]::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Set the date to today";
}
.bootstrap-datetimepicker-widget .picker-switch {
text-align: center;
}
.bootstrap-datetimepicker-widget .picker-switch::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Toggle Date and Time Screens";
}
.bootstrap-datetimepicker-widget .picker-switch td {
padding: 0;
margin: 0;
height: auto;
width: auto;
line-height: inherit;
}
.bootstrap-datetimepicker-widget .picker-switch td span {
line-height: 2.5;
height: 2.5em;
width: 100%;
}
.bootstrap-datetimepicker-widget table {
width: 100%;
margin: 0;
}
.bootstrap-datetimepicker-widget table td,
.bootstrap-datetimepicker-widget table th {
text-align: center;
border-radius: 4px;
}
.bootstrap-datetimepicker-widget table th {
height: 20px;
line-height: 20px;
width: 20px;
}
.bootstrap-datetimepicker-widget table th.picker-switch {
width: 145px;
}
.bootstrap-datetimepicker-widget table th.disabled,
.bootstrap-datetimepicker-widget table th.disabled:hover {
background: none;
color: #777777;
cursor: not-allowed;
}
.bootstrap-datetimepicker-widget table th.prev::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Previous Month";
}
.bootstrap-datetimepicker-widget table th.next::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Next Month";
}
.bootstrap-datetimepicker-widget table thead tr:first-child th {
cursor: pointer;
}
.bootstrap-datetimepicker-widget table thead tr:first-child th:hover {
background: #eeeeee;
}
.bootstrap-datetimepicker-widget table td {
height: 54px;
line-height: 54px;
width: 54px;
}
.bootstrap-datetimepicker-widget table td.cw {
font-size: .8em;
height: 20px;
line-height: 20px;
color: #777777;
}
.bootstrap-datetimepicker-widget table td.day {
height: 20px;
line-height: 20px;
width: 20px;
}
.bootstrap-datetimepicker-widget table td.day:hover,
.bootstrap-datetimepicker-widget table td.hour:hover,
.bootstrap-datetimepicker-widget table td.minute:hover,
.bootstrap-datetimepicker-widget table td.second:hover {
background: #eeeeee;
cursor: pointer;
}
.bootstrap-datetimepicker-widget table td.old,
.bootstrap-datetimepicker-widget table td.new {
color: #777777;
}
.bootstrap-datetimepicker-widget table td.today {
position: relative;
}
.bootstrap-datetimepicker-widget table td.today:before {
content: '';
display: inline-block;
border: 0 0 7px 7px solid transparent;
border-bottom-color: #337ab7;
border-top-color: rgba(0, 0, 0, 0.2);
position: absolute;
bottom: 4px;
right: 4px;
}
.bootstrap-datetimepicker-widget table td.active,
.bootstrap-datetimepicker-widget table td.active:hover {
background-color: #337ab7;
color: #ffffff;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.bootstrap-datetimepicker-widget table td.active.today:before {
border-bottom-color: #fff;
}
.bootstrap-datetimepicker-widget table td.disabled,
.bootstrap-datetimepicker-widget table td.disabled:hover {
background: none;
color: #777777;
cursor: not-allowed;
}
.bootstrap-datetimepicker-widget table td span {
display: inline-block;
width: 54px;
height: 54px;
line-height: 54px;
margin: 2px 1.5px;
cursor: pointer;
border-radius: 4px;
}
.bootstrap-datetimepicker-widget table td span:hover {
background: #eeeeee;
}
.bootstrap-datetimepicker-widget table td span.active {
background-color: #337ab7;
color: #ffffff;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.bootstrap-datetimepicker-widget table td span.old {
color: #777777;
}
.bootstrap-datetimepicker-widget table td span.disabled,
.bootstrap-datetimepicker-widget table td span.disabled:hover {
background: none;
color: #777777;
cursor: not-allowed;
}
.bootstrap-datetimepicker-widget.usetwentyfour td.hour {
height: 27px;
line-height: 27px;
}
.input-group.date .input-group-addon {
cursor: pointer;
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
}
File diff suppressed because one or more lines are too long
+2 -3
View File
@@ -1,10 +1,9 @@
{
"name": "bootstrap-datetimepicker",
"version": "3.1.3",
"main": ["build/css/bootstrap-datetimepicker.min.css","build/js/bootstrap-datetimepicker.min.js"],
"version": "4.7.14",
"main": ["build/css/bootstrap-datetimepicker.min.css","build/js/bootstrap-datetimepicker.min.js","src/less/_bootstrap-datetimepicker.less","src/less/bootstrap-datetimepicker-build.less","src/js/bootstrap-datetimepicker.js"],
"dependencies": {
"jquery" : ">=1.8.3",
"bootstrap" : ">=3.0",
"moment": ">=2.8.0"
}
}
+4 -3
View File
@@ -1,7 +1,7 @@
{
"name": "eonasdan/bootstrap-datetimepicker",
"type": "component",
"version": "3.1.3",
"version": "4.7.14",
"description": "Date/time picker widget based on twitter bootstrap",
"keywords": [
"bootstrap",
@@ -12,7 +12,6 @@
"require": {
"robloach/component-installer": "*",
"components/jquery": ">=1.9.1",
"components/bootstrap": "3.*",
"moment/moment": ">=2.8"
},
"extra": {
@@ -23,7 +22,9 @@
"files": [
"build/js/bootstrap-datetimepicker.min.js",
"build/css/bootstrap-datetimepicker.css",
"build/css/bootstrap-datetimepicker.min.css"
"build/css/bootstrap-datetimepicker.min.css",
"src/less/_bootstrap-datetimepicker.less",
"src/less/bootstrap-datetimepicker-build.less"
]
}
}
+8 -8
View File
@@ -1,7 +1,7 @@
{
"name": "bootstrap-datetimepicker",
"main": "src/js/bootstrap-datetimepicker.js",
"version": "3.1.3",
"version": "4.7.14",
"repository": {
"type": "git",
"url": "https://github.com/eonasdan/bootstrap-datetimepicker.git"
@@ -15,21 +15,21 @@
"datepicker",
"datetimepicker",
"timepicker",
"moment"
"moment"
],
"dependencies": {
"moment": "~2.8.1",
"bootstrap": "^3.0",
"jquery": "^1.8.3"
"moment": "~2.8.2",
"bootstrap": "^3.0",
"jquery": "latest"
},
"devDependencies": {
"grunt": "latest",
"grunt-contrib-jasmine": "^0.7.0",
"grunt-contrib-jshint": "latest",
"grunt-contrib-less": "latest",
"grunt-contrib-uglify": "latest",
"grunt-jscs": "latest",
"load-grunt-tasks": "latest",
"grunt-string-replace": "latest",
"grunt-contrib-less": "latest",
"bootstrap": "latest"
"load-grunt-tasks": "latest"
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,343 @@
/*!
* Datetimepicker for Bootstrap 3
* ! version : 4.7.14
* https://github.com/Eonasdan/bootstrap-datetimepicker/
*/
@bs-datetimepicker-timepicker-font-size: 1.2em;
@bs-datetimepicker-active-bg: @btn-primary-bg;
@bs-datetimepicker-active-color: @btn-primary-color;
@bs-datetimepicker-border-radius: @border-radius-base;
@bs-datetimepicker-btn-hover-bg: @gray-lighter;
@bs-datetimepicker-disabled-color: @gray-light;
@bs-datetimepicker-alternate-color: @gray-light;
@bs-datetimepicker-secondary-border-color: #ccc;
@bs-datetimepicker-secondary-border-color-rgba: rgba(0, 0, 0, 0.2);
@bs-datetimepicker-primary-border-color: white;
@bs-datetimepicker-text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
.bootstrap-datetimepicker-widget {
list-style: none;
&.dropdown-menu {
margin: 2px 0;
padding: 4px;
width: 19em;
&.timepicker-sbs {
@media (min-width: @screen-sm-min) {
width: 38em;
}
@media (min-width: @screen-md-min) {
width: 38em;
}
@media (min-width: @screen-lg-min) {
width: 38em;
}
}
&:before, &:after {
content: '';
display: inline-block;
position: absolute;
}
&.bottom {
&:before {
border-left: 7px solid transparent;
border-right: 7px solid transparent;
border-bottom: 7px solid @bs-datetimepicker-secondary-border-color;
border-bottom-color: @bs-datetimepicker-secondary-border-color-rgba;
top: -7px;
left: 7px;
}
&:after {
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-bottom: 6px solid @bs-datetimepicker-primary-border-color;
top: -6px;
left: 8px;
}
}
&.top {
&:before {
border-left: 7px solid transparent;
border-right: 7px solid transparent;
border-top: 7px solid @bs-datetimepicker-secondary-border-color;
border-top-color: @bs-datetimepicker-secondary-border-color-rgba;
bottom: -7px;
left: 6px;
}
&:after {
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-top: 6px solid @bs-datetimepicker-primary-border-color;
bottom: -6px;
left: 7px;
}
}
&.pull-right {
&:before {
left: auto;
right: 6px;
}
&:after {
left: auto;
right: 7px;
}
}
}
.list-unstyled {
margin: 0;
}
a[data-action] {
padding: 6px 0;
}
a[data-action]:active {
box-shadow: none;
}
.timepicker-hour, .timepicker-minute, .timepicker-second {
width: 54px;
font-weight: bold;
font-size: @bs-datetimepicker-timepicker-font-size;
margin: 0;
}
button[data-action] {
padding: 6px;
}
.btn[data-action="incrementHours"]::after {
.sr-only();
content: "Increment Hours";
}
.btn[data-action="incrementMinutes"]::after {
.sr-only();
content: "Increment Minutes";
}
.btn[data-action="decrementHours"]::after {
.sr-only();
content: "Decrement Hours";
}
.btn[data-action="decrementMinutes"]::after {
.sr-only();
content: "Decrement Minutes";
}
.btn[data-action="showHours"]::after {
.sr-only();
content: "Show Hours";
}
.btn[data-action="showMinutes"]::after {
.sr-only();
content: "Show Minutes";
}
.btn[data-action="togglePeriod"]::after {
.sr-only();
content: "Toggle AM/PM";
}
.btn[data-action="clear"]::after {
.sr-only();
content: "Clear the picker";
}
.btn[data-action="today"]::after {
.sr-only();
content: "Set the date to today";
}
.picker-switch {
text-align: center;
&::after {
.sr-only();
content: "Toggle Date and Time Screens";
}
td {
padding: 0;
margin: 0;
height: auto;
width: auto;
line-height: inherit;
span {
line-height: 2.5;
height: 2.5em;
width: 100%;
}
}
}
table {
width: 100%;
margin: 0;
& td,
& th {
text-align: center;
border-radius: @bs-datetimepicker-border-radius;
}
& th {
height: 20px;
line-height: 20px;
width: 20px;
&.picker-switch {
width: 145px;
}
&.disabled,
&.disabled:hover {
background: none;
color: @bs-datetimepicker-disabled-color;
cursor: not-allowed;
}
&.prev::after {
.sr-only();
content: "Previous Month";
}
&.next::after {
.sr-only();
content: "Next Month";
}
}
& thead tr:first-child th {
cursor: pointer;
&:hover {
background: @bs-datetimepicker-btn-hover-bg;
}
}
& td {
height: 54px;
line-height: 54px;
width: 54px;
&.cw {
font-size: .8em;
height: 20px;
line-height: 20px;
color: @bs-datetimepicker-alternate-color;
}
&.day {
height: 20px;
line-height: 20px;
width: 20px;
}
&.day:hover,
&.hour:hover,
&.minute:hover,
&.second:hover {
background: @bs-datetimepicker-btn-hover-bg;
cursor: pointer;
}
&.old,
&.new {
color: @bs-datetimepicker-alternate-color;
}
&.today {
position: relative;
&:before {
content: '';
display: inline-block;
border: 0 0 7px 7px solid transparent;
border-bottom-color: @bs-datetimepicker-active-bg;
border-top-color: @bs-datetimepicker-secondary-border-color-rgba;
position: absolute;
bottom: 4px;
right: 4px;
}
}
&.active,
&.active:hover {
background-color: @bs-datetimepicker-active-bg;
color: @bs-datetimepicker-active-color;
text-shadow: @bs-datetimepicker-text-shadow;
}
&.active.today:before {
border-bottom-color: #fff;
}
&.disabled,
&.disabled:hover {
background: none;
color: @bs-datetimepicker-disabled-color;
cursor: not-allowed;
}
span {
display: inline-block;
width: 54px;
height: 54px;
line-height: 54px;
margin: 2px 1.5px;
cursor: pointer;
border-radius: @bs-datetimepicker-border-radius;
&:hover {
background: @bs-datetimepicker-btn-hover-bg;
}
&.active {
background-color: @bs-datetimepicker-active-bg;
color: @bs-datetimepicker-active-color;
text-shadow: @bs-datetimepicker-text-shadow;
}
&.old {
color: @bs-datetimepicker-alternate-color;
}
&.disabled,
&.disabled:hover {
background: none;
color: @bs-datetimepicker-disabled-color;
cursor: not-allowed;
}
}
}
}
&.usetwentyfour {
td.hour {
height: 27px;
line-height: 27px;
}
}
}
.input-group.date {
& .input-group-addon {
cursor: pointer;
}
}
@@ -1,5 +1,17 @@
// Import boostrap variables including default color palette and fonts
// Import bootstrap variables including default color palette and fonts
@import "../../node_modules/bootstrap/less/variables.less";
// Import datepicker component
@import "bootstrap-datetimepicker.less";
@import "_bootstrap-datetimepicker.less";
.sr-only {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0,0,0,0);
border: 0;
}
-294
View File
@@ -1,294 +0,0 @@
/*!
* Datetimepicker for Bootstrap v3
//! version : 3.1.3
* https://github.com/Eonasdan/bootstrap-datetimepicker/
*/
.bootstrap-datetimepicker-widget {
top: 0;
left: 0;
width: 250px;
padding: 4px;
margin-top: 1px;
z-index: 99999 !important;
border-radius: @border-radius-base;
&.timepicker-sbs {
width: 600px;
}
&.bottom {
&: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,.2);
position: absolute;
top: -7px;
left: 7px;
}
&:after {
content: '';
display: inline-block;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-bottom: 6px solid white;
position: absolute;
top: -6px;
left: 8px;
}
}
&.top {
&:before {
content: '';
display: inline-block;
border-left: 7px solid transparent;
border-right: 7px solid transparent;
border-top: 7px solid #ccc;
border-top-color: rgba(0,0,0,.2);
position: absolute;
bottom: -7px;
left: 6px;
}
&:after {
content: '';
display: inline-block;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-top: 6px solid white;
position: absolute;
bottom: -6px;
left: 7px;
}
}
& .dow {
width: 14.2857%;
}
&.pull-right {
&:before {
left: auto;
right: 6px;
}
&:after {
left: auto;
right: 7px;
}
}
>ul {
list-style-type: none;
margin: 0;
}
a[data-action] {
padding: 6px 0;
}
a[data-action]:active {
box-shadow: none;
}
.timepicker-hour, .timepicker-minute, .timepicker-second {
width: 54px;
font-weight: bold;
font-size: 1.2em;
margin: 0;
}
button[data-action] {
padding: 6px;
}
table[data-hour-format="12"] .separator {
width: 4px;
padding: 0;
margin: 0;
}
.datepicker > div {
display: none;
}
.picker-switch {
text-align: center;
}
table {
width: 100%;
margin: 0;
}
td,
th {
text-align: center;
border-radius: @border-radius-base;
}
td {
height: 54px;
line-height: 54px;
width: 54px;
&.cw
{
font-size: 10px;
height: 20px;
line-height: 20px;
color: @gray-light;
}
&.day
{
height: 20px;
line-height: 20px;
width: 20px;
}
&.day:hover,
&.hour:hover,
&.minute:hover,
&.second:hover {
background: @gray-lighter;
cursor: pointer;
}
&.old,
&.new {
color: @gray-light;
}
&.today {
position: relative;
&:before {
content: '';
display: inline-block;
border-left: 7px solid transparent;
border-bottom: 7px solid @btn-primary-bg;
border-top-color: rgba(0, 0, 0, 0.2);
position: absolute;
bottom: 4px;
right: 4px;
}
}
&.active,
&.active:hover {
background-color: @btn-primary-bg;
color: @btn-primary-color;
text-shadow: 0 -1px 0 rgba(0,0,0,.25);
}
&.active.today:before {
border-bottom-color: #fff;
}
&.disabled,
&.disabled:hover {
background: none;
color: @gray-light;
cursor: not-allowed;
}
span {
display: inline-block;
width: 54px;
height: 54px;
line-height: 54px;
margin: 2px 1.5px;
cursor: pointer;
border-radius: @border-radius-base;
&:hover {
background: @gray-lighter;
}
&.active {
background-color: @btn-primary-bg;
color: @btn-primary-color;
text-shadow: 0 -1px 0 rgba(0,0,0,.25);
}
&.old {
color: @gray-light;
}
&.disabled,
&.disabled:hover {
background: none;
color: @gray-light;
cursor: not-allowed;
}
}
}
th {
height: 20px;
line-height: 20px;
width: 20px;
&.picker-switch {
width: 145px;
}
&.next,
&.prev {
font-size: @font-size-base * 1.5;
}
&.disabled,
&.disabled:hover {
background: none;
color: @gray-light;
cursor: not-allowed;
}
}
thead tr:first-child th {
cursor: pointer;
&:hover {
background: @gray-lighter;
}
}
}
.input-group {
&.date {
.input-group-addon span {
display: block;
cursor: pointer;
width: 16px;
height: 16px;
}
}
}
.bootstrap-datetimepicker-widget.left-oriented {
&:before {
left: auto;
right: 6px;
}
&:after {
left: auto;
right: 7px;
}
}
.bootstrap-datetimepicker-widget ul.list-unstyled li div.timepicker div.timepicker-picker table.table-condensed tbody > tr > td {
padding: 0px !important;
}
@media screen and (max-width: 767px) {
.bootstrap-datetimepicker-widget.timepicker-sbs {
width: 283px;
}
}
@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>Bootstrap.v3.Datetimepicker.CSS</id>
<version>3.1.2</version>
<version>4.0.0</version>
<title>Bootstrap 3 Datetimepicker CSS</title>
<authors>Eonasdan</authors>
<owners>Eonasdan</owners>
@@ -12,12 +12,12 @@
For usage, installation and demos see Project Site on GitHub
For CSS version install Bootstrap.v3.Datetimepicker.CSS</description>
For LESS version install Bootstrap.v3.Datetimepicker</description>
<releaseNotes>Check the change log on Github at https://github.com/Eonasdan/bootstrap-datetimepicker/wiki/Change-Log</releaseNotes>
<tags>bootstrap date time picker datetimepicker datepicker jquery</tags>
<dependencies>
<dependency id="Twitter.Bootstrap.Less" version="3.0.0" />
<dependency id="Moment.js" version="2.8.1" />
<dependency id="Moment.js" version="2.8.4" />
</dependencies>
</metadata>
<files>
@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>Bootstrap.v3.Datetimepicker</id>
<version>3.1.2</version>
<version>4.0.0</version>
<title>Bootstrap 3 Datetimepicker</title>
<authors>Eonasdan</authors>
<owners>Eonasdan</owners>
@@ -17,13 +17,13 @@ For CSS version install Bootstrap.v3.Datetimepicker.CSS</description>
<tags>bootstrap date time picker datetimepicker datepicker jquery</tags>
<dependencies>
<dependency id="Twitter.Bootstrap.Less" version="3.0.0" />
<dependency id="Moment.js" version="2.8.1" />
<dependency id="Moment.js" version="2.8.4" />
</dependencies>
</metadata>
<files>
<file src="..\..\src\js\bootstrap-datetimepicker.js" target="content\Scripts" />
<file src="..\..\build\js\bootstrap-datetimepicker.min.js" target="content\Scripts" />
<file src="..\..\src\less\bootstrap-datetimepicker.less" target="content\Content\less" />
<file src="..\..\src\less\_bootstrap-datetimepicker.less" target="content\Content\less" />
<file src="..\..\src\less\bootstrap-datetimepicker-build.less" target="content\Content\less" />
<file src="install.ps1" target="tools\" />
</files>
+1 -1
View File
@@ -1,2 +1,2 @@
# install.ps1
$DTE.ItemOperations.Navigate("https://github.com/Eonasdan/bootstrap-datetimepicker#change-log", $DTE.vsNavigateOptions.vsNavigateOptionsNewWindow)
$DTE.ItemOperations.Navigate("http://eonasdan.github.io/bootstrap-datetimepicker/", $DTE.vsNavigateOptions.vsNavigateOptionsNewWindow)
+700
View File
@@ -0,0 +1,700 @@
describe('Plugin initialization and component basic construction', function () {
'use strict';
it('loads jquery plugin properly', function () {
expect($('<div>').datetimepicker).toBeDefined();
expect(typeof $('<div>').datetimepicker).toEqual('function');
expect($('<div>').datetimepicker.defaults).toBeDefined();
});
it('creates the component with default options on an input element', function () {
var dtp = $('<input>');
$(document).find('body').append(dtp);
expect(function () {
dtp = dtp.datetimepicker();
}).not.toThrow();
expect(dtp).not.toBe(null);
});
xit('calls destroy when Element that the component is attached is removed', function () {
var dtpElement = $('<div>').attr('class', 'row').append($('<div>').attr('class', 'col-md-12').append($('<input>'))),
dtp;
$(document).find('body').append(dtpElement);
dtpElement.datetimepicker();
dtp = dtpElement.data('DateTimePicker');
spyOn(dtp, 'destroy').and.callThrough();
dtpElement.remove();
expect(dtp.destroy).toHaveBeenCalled();
});
});
describe('Public API method tests', function () {
'use strict';
var dtp,
dtpElement,
dpChangeSpy,
dpShowSpy,
dpHideSpy,
dpErrorSpy;
beforeEach(function () {
dpChangeSpy = jasmine.createSpy('dp.change event Spy');
dpShowSpy = jasmine.createSpy('dp.show event Spy');
dpHideSpy = jasmine.createSpy('dp.hide event Spy');
dpErrorSpy = jasmine.createSpy('dp.error event Spy');
dtpElement = $('<input>').attr('id', 'dtp');
$(document).find('body').append($('<div>').attr('class', 'row').append($('<div>').attr('class', 'col-md-12').append(dtpElement)));
$(document).find('body').on('dp.change', dpChangeSpy);
$(document).find('body').on('dp.show', dpShowSpy);
$(document).find('body').on('dp.hide', dpHideSpy);
$(document).find('body').on('dp.error', dpErrorSpy);
dtpElement.datetimepicker();
dtp = dtpElement.data('DateTimePicker');
});
afterEach(function () {
dtp.destroy();
dtpElement.remove();
});
describe('configuration option name match to public api function', function () {
Object.getOwnPropertyNames($.fn.datetimepicker.defaults).forEach(function (key) {
it('has function ' + key + '()', function () {
expect(dtp[key]).toBeDefined();
});
});
});
describe('date() function', function () {
describe('typechecking', function () {
it('accepts a null', function () {
expect(function () {
dtp.date(null);
}).not.toThrow();
});
it('accepts a string', function () {
expect(function () {
dtp.date('2013/05/24');
}).not.toThrow();
});
it('accepts a Date object', function () {
expect(function () {
dtp.date(new Date());
}).not.toThrow();
});
it('accepts a Moment object', function () {
expect(function () {
dtp.date(moment());
}).not.toThrow();
});
it('does not accept undefined', function () {
expect(function () {
dtp.date(undefined);
}).toThrow();
});
it('does not accept a number', function () {
expect(function () {
dtp.date(0);
}).toThrow();
});
it('does not accept a generic Object', function () {
expect(function () {
dtp.date({});
}).toThrow();
});
it('does not accept a boolean', function () {
expect(function () {
dtp.date(false);
}).toThrow();
});
});
describe('functionality', function () {
it('has no date set upon construction', function () {
expect(dtp.date()).toBe(null);
});
it('sets the date correctly', function () {
var timestamp = moment();
dtp.date(timestamp);
expect(dtp.date().isSame(timestamp)).toBe(true);
});
});
});
describe('format() function', function () {
describe('typechecking', function () {
it('accepts a false value', function () {
expect(function () {
dtp.format(false);
}).not.toThrow();
});
it('accepts a string', function () {
expect(function () {
dtp.format('YYYY-MM-DD');
}).not.toThrow();
});
it('does not accept undefined', function () {
expect(function () {
dtp.format(undefined);
}).toThrow();
});
it('does not accept true', function () {
expect(function () {
dtp.format(true);
}).toThrow();
});
it('does not accept a generic Object', function () {
expect(function () {
dtp.format({});
}).toThrow();
});
});
describe('functionality', function () {
it('returns no format before format is set', function () {
expect(dtp.format()).toBe(false);
});
it('sets the format correctly', function () {
dtp.format('YYYY-MM-DD');
expect(dtp.format()).toBe('YYYY-MM-DD');
});
});
});
describe('destroy() function', function () {
describe('existence', function () {
it('is defined', function () {
expect(dtp.destroy).toBeDefined();
});
});
});
describe('toggle() function', function () {
describe('existence', function () {
it('is defined', function () {
expect(dtp.toggle).toBeDefined();
});
});
// describe('functionality', function () {
// it('')
// });
});
describe('show() function', function () {
describe('existence', function () {
it('is defined', function () {
expect(dtp.show).toBeDefined();
});
});
describe('functionality', function () {
it('emits a show event when called while widget is hidden', function () {
dtp.show();
expect(dpShowSpy).toHaveBeenCalled();
});
it('does not emit a show event when called and widget is already showing', function () {
dtp.hide();
dtp.show();
dpShowSpy.calls.reset();
dtp.show();
expect(dpShowSpy).not.toHaveBeenCalled();
});
it('actually shows the widget', function () {
dtp.show();
expect($(document).find('body').find('.bootstrap-datetimepicker-widget').length).toEqual(1);
});
});
});
describe('hide() function', function () {
describe('existence', function () {
it('is defined', function () {
expect(dtp.hide).toBeDefined();
});
});
describe('functionality', function () {
it('emits a hide event when called while widget is shown', function () {
dtp.show();
dtp.hide();
expect(dpHideSpy).toHaveBeenCalled();
});
it('does not emit a hide event when called while widget is hidden', function () {
dtp.hide();
expect(dpHideSpy).not.toHaveBeenCalled();
});
it('actually hides the widget', function () {
dtp.show();
dtp.hide();
expect($(document).find('body').find('.bootstrap-datetimepicker-widget').length).toEqual(0);
});
});
});
describe('disable() function', function () {
describe('existence', function () {
it('is defined', function () {
expect(dtp.disable).toBeDefined();
});
});
});
describe('enable() function', function () {
describe('existence', function () {
it('is defined', function () {
expect(dtp.enable).toBeDefined();
});
});
});
describe('options() function', function () {
describe('existence', function () {
it('is defined', function () {
expect(dtp.options).toBeDefined();
});
});
});
describe('disabledDates() function', function () {
describe('existence', function () {
it('is defined', function () {
expect(dtp.disabledDates).toBeDefined();
});
});
});
describe('enabledDates() function', function () {
describe('existence', function () {
it('is defined', function () {
expect(dtp.enabledDates).toBeDefined();
});
});
});
describe('daysOfWeekDisabled() function', function () {
describe('existence', function () {
it('is defined', function () {
expect(dtp.daysOfWeekDisabled).toBeDefined();
});
});
});
describe('maxDate() function', function () {
describe('existence', function () {
it('is defined', function () {
expect(dtp.maxDate).toBeDefined();
});
});
});
describe('minDate() function', function () {
describe('existence', function () {
it('is defined', function () {
expect(dtp.minDate).toBeDefined();
});
});
});
describe('defaultDate() function', function () {
describe('existence', function () {
it('is defined', function () {
expect(dtp.defaultDate).toBeDefined();
});
});
describe('functionality', function () {
it('returns no defaultDate before defaultDate is set', function () {
expect(dtp.defaultDate()).toBe(false);
});
it('sets the defaultDate correctly', function () {
var timestamp = moment();
dtp.defaultDate(timestamp);
expect(dtp.defaultDate().isSame(timestamp)).toBe(true);
expect(dtp.date().isSame(timestamp)).toBe(true);
});
it('triggers a change event upon setting a default date and input field is empty', function () {
dtp.date(null);
dtp.defaultDate(moment());
expect(dpChangeSpy).toHaveBeenCalled();
});
it('does not override input value if it already has one', function () {
var timestamp = moment();
dtp.date(timestamp);
dtp.defaultDate(moment().year(2000));
expect(dtp.date().isSame(timestamp)).toBe(true);
});
});
});
describe('locale() function', function () {
describe('functionality', function () {
it('it has the same locale as the global moment locale with default options', function () {
expect(dtp.locale()).toBe(moment.locale());
});
it('it switches to a selected locale without affecting global moment locale', function () {
dtp.locale('el');
dtp.date(moment());
expect(dtp.locale()).toBe('el');
expect(dtp.date().locale()).toBe('el');
expect(moment.locale()).toBe('en');
});
});
});
describe('useCurrent() function', function () {
describe('existence', function () {
it('is defined', function () {
expect(dtp.useCurrent).toBeDefined();
});
});
describe('check type and parameter validity', function () {
it('accepts either a boolean value or string', function () {
var useCurrentOptions = ['year', 'month', 'day', 'hour', 'minute'];
expect(function () {
dtp.useCurrent(false);
}).not.toThrow();
expect(function () {
dtp.useCurrent(true);
}).not.toThrow();
useCurrentOptions.forEach(function (value) {
expect(function () {
dtp.useCurrent(value);
}).not.toThrow();
});
expect(function () {
dtp.useCurrent('test');
}).toThrow();
expect(function () {
dtp.useCurrent({});
}).toThrow();
});
});
describe('functionality', function () {
it('triggers a change event upon show() and input field is empty', function () {
dtp.useCurrent(true);
dtp.show();
expect(dpChangeSpy).toHaveBeenCalled();
});
});
});
describe('ignoreReadonly() function', function () {
describe('existence', function () {
it('is defined', function () {
expect(dtp.ignoreReadonly).toBeDefined();
});
});
});
describe('stepping() function', function () {
describe('existence', function () {
it('is defined', function () {
expect(dtp.stepping).toBeDefined();
});
});
});
describe('collapse() function', function () {
describe('existence', function () {
it('is defined', function () {
expect(dtp.collapse).toBeDefined();
});
});
});
describe('icons() function', function () {
describe('existence', function () {
it('is defined', function () {
expect(dtp.icons).toBeDefined();
});
});
});
describe('useStrict() function', function () {
describe('existence', function () {
it('is defined', function () {
expect(dtp.useStrict).toBeDefined();
});
});
});
describe('sideBySide() function', function () {
describe('existence', function () {
it('is defined', function () {
expect(dtp.sideBySide).toBeDefined();
});
});
});
describe('viewMode() function', function () {
describe('existence', function () {
it('is defined', function () {
expect(dtp.viewMode).toBeDefined();
});
});
});
describe('widgetPositioning() function', function () {
describe('existence', function () {
it('is defined', function () {
expect(dtp.widgetPositioning).toBeDefined();
});
});
});
describe('calendarWeeks() function', function () {
describe('existence', function () {
it('is defined', function () {
expect(dtp.calendarWeeks).toBeDefined();
});
});
});
describe('showTodayButton() function', function () {
describe('existence', function () {
it('is defined', function () {
expect(dtp.showTodayButton).toBeDefined();
});
});
});
describe('showClear() function', function () {
describe('existence', function () {
it('is defined', function () {
expect(dtp.showClear).toBeDefined();
});
});
});
describe('dayViewHeaderFormat() function', function () {
describe('typechecking', function () {
it('does not accept a false value', function () {
expect(function () {
dtp.dayViewHeaderFormat(false);
}).toThrow();
});
it('accepts a string', function () {
expect(function () {
dtp.dayViewHeaderFormat('YYYY-MM-DD');
}).not.toThrow();
});
it('does not accept undefined', function () {
expect(function () {
dtp.dayViewHeaderFormat(undefined);
}).toThrow();
});
it('does not accept true', function () {
expect(function () {
dtp.dayViewHeaderFormat(true);
}).toThrow();
});
it('does not accept a generic Object', function () {
expect(function () {
dtp.dayViewHeaderFormat({});
}).toThrow();
});
});
describe('functionality', function () {
it('expects dayViewHeaderFormat to be default of MMMM YYYY', function () {
expect(dtp.dayViewHeaderFormat()).toBe('MMMM YYYY');
});
it('sets the dayViewHeaderFormat correctly', function () {
dtp.dayViewHeaderFormat('MM YY');
expect(dtp.dayViewHeaderFormat()).toBe('MM YY');
});
});
});
describe('extraFormats() function', function () {
describe('typechecking', function () {
it('accepts a false value', function () {
expect(function () {
dtp.extraFormats(false);
}).not.toThrow();
});
it('does not accept a string', function () {
expect(function () {
dtp.extraFormats('YYYY-MM-DD');
}).toThrow();
});
it('does not accept undefined', function () {
expect(function () {
dtp.extraFormats(undefined);
}).toThrow();
});
it('does not accept true', function () {
expect(function () {
dtp.extraFormats(true);
}).toThrow();
});
it('accepts an Array', function () {
expect(function () {
dtp.extraFormats(['YYYY-MM-DD']);
}).not.toThrow();
});
});
describe('functionality', function () {
it('returns no extraFormats before extraFormats is set', function () {
expect(dtp.extraFormats()).toBe(false);
});
it('sets the extraFormats correctly', function () {
dtp.extraFormats(['YYYY-MM-DD']);
expect(dtp.extraFormats()[0]).toBe('YYYY-MM-DD');
});
});
});
describe('toolbarPlacement() function', function () {
describe('existence', function () {
it('is defined', function () {
expect(dtp.toolbarPlacement).toBeDefined();
});
});
describe('check type and parameter validity', function () {
it('does not accept a false value', function () {
expect(function () {
dtp.dayViewHeaderFormat(false);
}).toThrow();
});
it('does not accept a false value', function () {
expect(function () {
dtp.dayViewHeaderFormat(false);
}).toThrow();
});
it('accepts a string', function () {
var toolbarPlacementOptions = ['default', 'top', 'bottom'];
toolbarPlacementOptions.forEach(function (value) {
expect(function () {
dtp.toolbarPlacement(value);
}).not.toThrow();
});
expect(function () {
dtp.toolbarPlacement('test');
}).toThrow();
expect(function () {
dtp.toolbarPlacement({});
}).toThrow();
});
});
});
describe('widgetParent() function', function () {
describe('typechecking', function () {
it('accepts a null', function () {
expect(function () {
dtp.widgetParent(null);
}).not.toThrow();
});
it('accepts a string', function () {
expect(function () {
dtp.widgetParent('testDiv');
}).not.toThrow();
});
it('accepts a jquery object', function () {
expect(function () {
dtp.widgetParent($('#testDiv'));
}).not.toThrow();
});
it('does not accept undefined', function () {
expect(function () {
dtp.widgetParent(undefined);
}).toThrow();
});
it('does not accept a number', function () {
expect(function () {
dtp.widgetParent(0);
}).toThrow();
});
it('does not accept a generic Object', function () {
expect(function () {
dtp.widgetParent({});
}).toThrow();
});
it('does not accept a boolean', function () {
expect(function () {
dtp.widgetParent(false);
}).toThrow();
});
});
});
describe('keepOpen() function', function () {
describe('existence', function () {
it('is defined', function () {
expect(dtp.keepOpen).toBeDefined();
});
});
});
describe('inline() function', function () {
describe('existence', function () {
it('is defined', function () {
expect(dtp.inline).toBeDefined();
});
});
});
describe('clear() function', function () {
describe('existence', function () {
it('is defined', function () {
expect(dtp.clear).toBeDefined();
});
});
});
describe('keyBinds() function', function () {
describe('existence', function () {
it('is defined', function () {
expect(dtp.keyBinds).toBeDefined();
});
});
});
});
+98
View File
@@ -0,0 +1,98 @@
<?php
/*
* LibreNMS
*
* Copyright (c) 2014 Neil Lathwood <https://github.com/laf/ http://www.lathwood.co.uk/fa>
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version. Please see LICENSE.txt at the top level of
* the source code distribution for details.
*/
$pagetitle[] = "Alert Schedule";
$no_refresh = TRUE;
if(is_admin() !== false) {
require_once("includes/modal/alert_schedule.inc.php");
require_once("includes/modal/remove_alert_schedule.inc.php");
?>
<div class="row">
<div class="col-sm-12">
<span id="message"></span>
</div>
</div>
<div class="panel panel-default panel-condensed">
<div class="table-responsive">
<table id="alert-schedule" class="table table-condensed">
<thead>
<tr>
<th data-column-id="title">Title</th>
<th data-column-id="start" data-order="desc">Start</th>
<th data-column-id="end">End</th>
<th data-column-id="actions" data-sortable="false" data-searchable="false" data-formatter="commands">Actions</th>
</tr>
</thead>
</table>
</div>
</div>
<script>
var grid = $("#alert-schedule").bootgrid({
ajax: true,
formatters: {
"commands": function(column, row)
{
if (row.status == 1) {
return '<button type="button" class="btn btn-xs btn-danger" disabled>Lapsed</button>';
} else {
var response = "<button type=\"button\" class=\"btn btn-xs btn-primary command-edit\" data-toggle='modal' data-target='#schedule-maintenance' data-schedule_id=\"" + row.id + "\"><span class=\"fa fa-pencil\"></span></button> " +
"<button type=\"button\" class=\"btn btn-xs btn-danger command-delete\" data-schedule_id=\"" + row.id + "\"><span class=\"fa fa-trash-o\"></span></button>";
if (row.status == 2) {
response = response + ' <button type="button" class="btn btn-xs btn-success" disabled>Current</button>';
}
return response;
}
}
},
templates: {
header: "<div id=\"{{ctx.id}}\" class=\"{{css.header}}\"><div class=\"row\">"+
"<div class=\"col-sm-8 actionBar\"><span class=\"pull-left\">"+
"<button type=\"button\" class=\"btn btn-primary btn-sm\" data-toggle=\"modal\" data-target=\"#schedule-maintenance\">Schedule maintenance</button>"+
"</span></div>"+
"<div class=\"col-sm-4 actionBar\"><p class=\"{{css.search}}\"></p><p class=\"{{css.actions}}\"></p></div></div></div>"
},
rowCount: [50,100,250,-1],
post: function ()
{
return {
id: "alert-schedule",
};
},
url: "/ajax_table.php"
}).on("loaded.rs.jquery.bootgrid", function()
{
/* Executes after data is loaded and rendered */
grid.find(".command-edit").on("click", function(e)
{
$('#schedule_id').val($(this).data("schedule_id"));
$("#schedule-maintenance").modal('show');
}).end().find(".command-delete").on("click", function(e)
{
$('#del_schedule_id').val($(this).data("schedule_id"));
$('#delete-maintenance').modal('show');
});
});
</script>
<?php
}
?>
+2 -2
View File
@@ -156,8 +156,8 @@ if (!$auth)
</form>
<script type="text/javascript">
$(function () {
$("#dtpickerfrom").datetimepicker({useSeconds: false, useCurrent: true, sideBySide: true, useStrict: false, showToday: true});
$("#dtpickerto").datetimepicker({useSeconds: false, useCurrent: true, sideBySide: true, useStrict: false, showToday: true});
$("#dtpickerfrom").datetimepicker({useCurrent: true, sideBySide: true, useStrict: false});
$("#dtpickerto").datetimepicker({useCurrent: true, sideBySide: true, useStrict: false});
});
</script>
+18
View File
@@ -1154,3 +1154,21 @@ function set_curl_proxy($post)
curl_setopt($post, CURLOPT_PROXY, $config['callback_proxy']);
}
}
function target_to_id($target) {
if( $target[0].$target[1] == "g:" ) {
$target = "g".dbFetchCell('SELECT id FROM device_groups WHERE name = ?',array(substr($target,2)));
} else {
$target = dbFetchCell('SELECT device_id FROM devices WHERE hostname = ?',array($target));
}
return $target;
}
function id_to_target($id) {
if( $id[0] == "g" ) {
$id = 'g:'.dbFetchCell("SELECT name FROM device_groups WHERE id = ?",array(substr($id,1)));
} else {
$id = dbFetchCell("SELECT hostname FROM devices WHERE device_id = ?",array($id));
}
return $id;
}
+4
View File
@@ -0,0 +1,4 @@
ALTER TABLE `alert_schedule` DROP `device_id`;
ALTER TABLE `alert_schedule` CHANGE `id` `schedule_id` INT( 11 ) NOT NULL AUTO_INCREMENT;
ALTER TABLE `alert_schedule` ADD `title` VARCHAR( 255 ) NOT NULL ,ADD `notes` TEXT NOT NULL ;
CREATE TABLE `librenms`.`alert_schedule_items` (`item_id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,`schedule_id` INT NOT NULL ,`target` VARCHAR( 255 ) NOT NULL ,INDEX ( `schedule_id` )) ENGINE = INNODB;