var app = angular.module('plunker', ['ui.bootstrap.datetimepicker', 'ui.dateTimeInput', 'jcs-autoValidate'])
.run([
'bootstrap3ElementModifier',
function(bootstrap3ElementModifier) {
bootstrap3ElementModifier.enableValidationStateIcons(true);
}
]);
app.controller('MainCtrl', function($scope) {
$scope.dates = {};
$scope.name = 'DateTimePicker(http://dalelotts.github.io/angular-bootstrap-datetimepicker/) and for datetime formats (https://github.com/dalelotts/angular-date-time-input)';
moment.locale('en');
$scope.dates.AlarmDate = "11/17/2014 13:22:00";
//$scope.dates.ControlledDate = "11/18/2014 13:22:00";
//alert(0.222>0);
$scope.$watch('dates', function(newval, oldval) {
$scope.difference = dateDiff($scope.dates.AlarmDate, $scope.dates.ControlledDate);
if (dateDiff($scope.dates.AlarmDate, $scope.dates.ControlledDate) > 0) {
$scope.dateCheckForm.controlleddate.$invalid = true;
} else {
$scope.dateCheckForm.controlleddate.$invalid = false;
}
$scope.formValid = $scope.dateCheckForm.$valid;
}, true);
function dateDiff(dtFrom, dtTo) {
var dt1 = new Date(dtFrom);
var dt2 = new Date(dtTo);
var diff = dt2.getTime() - dt1.getTime();
var days = diff / (1000 * 60 * 60 * 24);
//alert(days);
return (days);
}
});
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>
document.write('<base href="' + document.location + '" />');
</script>
<link rel="stylesheet" href="style.css" />
<link rel="stylesheet" href="datetimepicker.css" />
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css" />
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js"></script>
<script data-require="angular.js@1.2.x" src="https://code.angularjs.org/1.2.25/angular.js" data-semver="1.2.25"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.8.3/moment.js"></script>
<script src="validate.js"></script>
<script src="datetimeinput.js"></script>
<script src="datetimepicker.js"></script>
<script src="app.js"></script>
<style>
.datetimepicker {
width: 400px;
}
</style>
</head>
<!--
* This works ;)
* I see on init it doesnt ! - dontnow scope doesnt seem to know about startdate on init
* Aaah !
-->
<body ng-controller="MainCtrl">
<samp class="red">Hello {{name}}!</samp>
<form name="dateCheckForm" novalidate="novalidate">
<hr/>
<div class='label' ng-class="dateCheckForm.controlleddate.$invalid?'label-danger':'label-success'">Valid?</div>
<kbd>Date Diff = {{difference}}</kbd>
<div class="well">
<p>Alarm Date: {{dates.AlarmDate | date: 'MM/dd/yyyy HH:mm:ss' }}</p>
<div class="dropdown">
<a class="dropdown-toggle" id="dropdown1" role="button" data-toggle="dropdown" data-target="#"
href="#">
<div class="input-group">
<input type="text" required data-date-time-input="MM/DD/YYYY HH:mm:ss" class="form-control" data-ng-model="dates.AlarmDate">
<span class="input-group-addon"><i class="glyphicon glyphicon-calendar"></i></span>
</div>
</a>
<ul class="dropdown-menu" role="menu" aria-labelledby="dLabel">
<datetimepicker data-ng-model="dates.AlarmDate"
data-datetimepicker-config="{ dropdownSelector: '#dropdown1' }"></datetimepicker>
</ul>
</div>
</div>
<hr/>
<div class="well form-group">
<p>Controlled Date: {{dates.ControlledDate | date: 'MM/dd/yyyy HH:mm:ss' }}</p>
<div class="dropdown" ng-class="{ 'has-error' : dateCheckForm.controlleddate.$invalid}">
<a class="dropdown-toggle" id="dropdown1" role="button" data-toggle="dropdown" data-target="#"
href="#">
<div class="input-group">
<input type="text" name='controlleddate' required data-date-time-input="MM/DD/YYYY HH:mm:ss" class="form-control" data-ng-model="dates.ControlledDate">
<span class="input-group-addon"><i class="glyphicon glyphicon-calendar"></i></span>
</div>
</a>
<ul class="dropdown-menu" role="menu" aria-labelledby="dLabel">
<datetimepicker data-ng-model="dates.ControlledDate"
data-datetimepicker-config="{ dropdownSelector: '#dropdown1' }"></datetimepicker>
</ul>
</div>
</div>
</form>
</body>
</html>
/* Put your css in here */
/**
* @license angular-bootstrap-datetimepicker version: 0.3.7
* (c) 2013-2014 Knight Rider Consulting, Inc. http://www.knightrider.com
* License: MIT
*/
.datetimepicker {
padding: 4px;
margin-top: 1px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
direction: ltr;
width: 320px;
}
.datetimepicker.datetimepicker-rtl {
direction: rtl;
}
.datetimepicker.datetimepicker-rtl table tr td span {
float: right;
}
.datetimepicker-dropdown, .datetimepicker-dropdown-left {
top: 0;
left: 0;
}
[class*=" datetimepicker-dropdown"]:before {
content: '';
display: inline-block;
border-left: 7px solid transparent;
border-right: 7px solid transparent;
border-bottom: 7px solid #ccc;
border-bottom-color: rgba(0, 0, 0, 0.2);
position: absolute;
}
[class*=" datetimepicker-dropdown"]:after {
content: '';
display: inline-block;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-bottom: 6px solid #ffffff;
position: absolute;
}
[class*=" datetimepicker-dropdown-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, 0.2);
border-bottom: 0;
}
[class*=" datetimepicker-dropdown-top"]:after {
content: '';
display: inline-block;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-top: 6px solid #ffffff;
border-bottom: 0;
}
.datetimepicker-dropdown-bottom-left:before {
top: -7px;
right: 6px;
}
.datetimepicker-dropdown-bottom-left:after {
top: -6px;
right: 7px;
}
.datetimepicker-dropdown-bottom-right:before {
top: -7px;
left: 6px;
}
.datetimepicker-dropdown-bottom-right:after {
top: -6px;
left: 7px;
}
.datetimepicker-dropdown-top-left:before {
bottom: -7px;
right: 6px;
}
.datetimepicker-dropdown-top-left:after {
bottom: -6px;
right: 7px;
}
.datetimepicker-dropdown-top-right:before {
bottom: -7px;
left: 6px;
}
.datetimepicker-dropdown-top-right:after {
bottom: -6px;
left: 7px;
}
.datetimepicker > div {
display: none;
}
.datetimepicker.minutes div.datetimepicker-minutes {
display: block;
}
.datetimepicker.hours div.datetimepicker-hours {
display: block;
}
.datetimepicker.days div.datetimepicker-days {
display: block;
}
.datetimepicker.months div.datetimepicker-months {
display: block;
}
.datetimepicker.years div.datetimepicker-years {
display: block;
}
.datetimepicker table {
margin: 0;
}
.datetimepicker .table td,
.datetimepicker .table th {
text-align: center;
width: 14.6%;
height: 20px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
border: none;
}
.datetimepicker .table-striped > tbody > tr:nth-child(odd) > td, .datetimepicker .table-striped > tbody > tr:nth-child(odd) > td,
.datetimepicker .table-striped > tbody > tr:nth-child(odd) > td, .datetimepicker .table-striped > tbody > tr:nth-child(odd) > th {
background-color: transparent;
}
.datetimepicker table tr td.minute:hover {
background: #eeeeee;
cursor: pointer;
}
.datetimepicker table tr td.hour:hover {
background: #eeeeee;
cursor: pointer;
}
.datetimepicker table tr td.day:hover {
background: #eeeeee;
cursor: pointer;
}
.datetimepicker table tr td.past,
.datetimepicker table tr td.future {
color: #999999;
}
.datetimepicker table tr td.disabled,
.datetimepicker table tr td.disabled:hover {
background: none;
color: #999999;
cursor: default;
}
.datetimepicker table tr td.today,
.datetimepicker table tr td.today:hover,
.datetimepicker table tr td.today.disabled,
.datetimepicker table tr td.today.disabled:hover {
background-color: #fde19a;
background-image: -moz-linear-gradient(top, #fdd49a, #fdf59a);
background-image: -ms-linear-gradient(top, #fdd49a, #fdf59a);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fdd49a), to(#fdf59a));
background-image: -webkit-linear-gradient(top, #fdd49a, #fdf59a);
background-image: -o-linear-gradient(top, #fdd49a, #fdf59a);
background-image: linear-gradient(top, #fdd49a, #fdf59a);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fdd49a', endColorstr='#fdf59a', GradientType=0);
border-color: #fdf59a #fdf59a #fbed50;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
}
.datetimepicker table tr td.today:hover,
.datetimepicker table tr td.today:hover:hover,
.datetimepicker table tr td.today.disabled:hover,
.datetimepicker table tr td.today.disabled:hover:hover,
.datetimepicker table tr td.today:active,
.datetimepicker table tr td.today:hover:active,
.datetimepicker table tr td.today.disabled:active,
.datetimepicker table tr td.today.disabled:hover:active,
.datetimepicker table tr td.today.active,
.datetimepicker table tr td.today:hover.active,
.datetimepicker table tr td.today.disabled.active,
.datetimepicker table tr td.today.disabled:hover.active,
.datetimepicker table tr td.today.disabled,
.datetimepicker table tr td.today:hover.disabled,
.datetimepicker table tr td.today.disabled.disabled,
.datetimepicker table tr td.today.disabled:hover.disabled,
.datetimepicker table tr td.today[disabled],
.datetimepicker table tr td.today:hover[disabled],
.datetimepicker table tr td.today.disabled[disabled],
.datetimepicker table tr td.today.disabled:hover[disabled] {
background-color: #fdf59a;
}
.datetimepicker table tr td.today:active,
.datetimepicker table tr td.today:hover:active,
.datetimepicker table tr td.today.disabled:active,
.datetimepicker table tr td.today.disabled:hover:active,
.datetimepicker table tr td.today.active,
.datetimepicker table tr td.today:hover.active,
.datetimepicker table tr td.today.disabled.active,
.datetimepicker table tr td.today.disabled:hover.active {
background-color: #fbf069 \9;
}
.datetimepicker table tr td.active,
.datetimepicker table tr td.active:hover,
.datetimepicker table tr td.active.disabled,
.datetimepicker table tr td.active.disabled:hover {
background-color: #006dcc;
background-image: -moz-linear-gradient(top, #0088cc, #0044cc);
background-image: -ms-linear-gradient(top, #0088cc, #0044cc);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));
background-image: -webkit-linear-gradient(top, #0088cc, #0044cc);
background-image: -o-linear-gradient(top, #0088cc, #0044cc);
background-image: linear-gradient(top, #0088cc, #0044cc);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0);
border-color: #0044cc #0044cc #002a80;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
color: #fff;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.datetimepicker table tr td.active:hover,
.datetimepicker table tr td.active:hover:hover,
.datetimepicker table tr td.active.disabled:hover,
.datetimepicker table tr td.active.disabled:hover:hover,
.datetimepicker table tr td.active:active,
.datetimepicker table tr td.active:hover:active,
.datetimepicker table tr td.active.disabled:active,
.datetimepicker table tr td.active.disabled:hover:active,
.datetimepicker table tr td.active.active,
.datetimepicker table tr td.active:hover.active,
.datetimepicker table tr td.active.disabled.active,
.datetimepicker table tr td.active.disabled:hover.active,
.datetimepicker table tr td.active.disabled,
.datetimepicker table tr td.active:hover.disabled,
.datetimepicker table tr td.active.disabled.disabled,
.datetimepicker table tr td.active.disabled:hover.disabled,
.datetimepicker table tr td.active[disabled],
.datetimepicker table tr td.active:hover[disabled],
.datetimepicker table tr td.active.disabled[disabled],
.datetimepicker table tr td.active.disabled:hover[disabled] {
background-color: #0044cc;
}
.datetimepicker table tr td.active:active,
.datetimepicker table tr td.active:hover:active,
.datetimepicker table tr td.active.disabled:active,
.datetimepicker table tr td.active.disabled:hover:active,
.datetimepicker table tr td.active.active,
.datetimepicker table tr td.active:hover.active,
.datetimepicker table tr td.active.disabled.active,
.datetimepicker table tr td.active.disabled:hover.active {
background-color: #003399 \9;
}
.datetimepicker table tr td span {
display: block;
width: 23%;
height: 54px;
line-height: 54px;
float: left;
margin: 1%;
cursor: pointer;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
}
.datetimepicker span.hour {
height: 26px;
line-height: 26px;
}
.datetimepicker span.minute {
height: 26px;
line-height: 26px;
}
.datetimepicker table tr td span:hover {
background: #eeeeee;
}
.datetimepicker table tr td span.disabled,
.datetimepicker table tr td span.disabled:hover {
background: none;
color: #999999;
cursor: default;
}
.datetimepicker table tr td span.active,
.datetimepicker table tr td span.active:hover,
.datetimepicker table tr td span.active.disabled,
.datetimepicker table tr td span.active.disabled:hover {
background-color: #006dcc;
background-image: -moz-linear-gradient(top, #0088cc, #0044cc);
background-image: -ms-linear-gradient(top, #0088cc, #0044cc);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));
background-image: -webkit-linear-gradient(top, #0088cc, #0044cc);
background-image: -o-linear-gradient(top, #0088cc, #0044cc);
background-image: linear-gradient(top, #0088cc, #0044cc);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0);
border-color: #0044cc #0044cc #002a80;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
color: #fff;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.datetimepicker table tr td span.active:hover,
.datetimepicker table tr td span.active:hover:hover,
.datetimepicker table tr td span.active.disabled:hover,
.datetimepicker table tr td span.active.disabled:hover:hover,
.datetimepicker table tr td span.active:active,
.datetimepicker table tr td span.active:hover:active,
.datetimepicker table tr td span.active.disabled:active,
.datetimepicker table tr td span.active.disabled:hover:active,
.datetimepicker table tr td span.active.active,
.datetimepicker table tr td span.active:hover.active,
.datetimepicker table tr td span.active.disabled.active,
.datetimepicker table tr td span.active.disabled:hover.active,
.datetimepicker table tr td span.active.disabled,
.datetimepicker table tr td span.active:hover.disabled,
.datetimepicker table tr td span.active.disabled.disabled,
.datetimepicker table tr td span.active.disabled:hover.disabled,
.datetimepicker table tr td span.active[disabled],
.datetimepicker table tr td span.active:hover[disabled],
.datetimepicker table tr td span.active.disabled[disabled],
.datetimepicker table tr td span.active.disabled:hover[disabled] {
background-color: #0044cc;
}
.datetimepicker table tr td span.active:active,
.datetimepicker table tr td span.active:hover:active,
.datetimepicker table tr td span.active.disabled:active,
.datetimepicker table tr td span.active.disabled:hover:active,
.datetimepicker table tr td span.active.active,
.datetimepicker table tr td span.active:hover.active,
.datetimepicker table tr td span.active.disabled.active,
.datetimepicker table tr td span.active.disabled:hover.active {
background-color: #003399 \9;
}
.datetimepicker table tr td span.past,
.datetimepicker table tr td span.future {
color: #999999;
}
.datetimepicker thead tr:first-child th,
.datetimepicker tfoot tr:first-child th {
cursor: pointer;
}
.datetimepicker thead tr:first-child th:hover,
.datetimepicker tfoot tr:first-child th:hover {
background: #eeeeee;
}
/*globals define, jQuery */
/*jslint vars:true */
/**
* @license angular-bootstrap-datetimepicker version: 0.3.7
* (c) 2013-2014 Knight Rider Consulting, Inc. http://www.knightrider.com
* License: MIT
*/
/**
*
* @author Dale "Ducky" Lotts
* @since 2013-Jul-8
*/
(function (factory) {
'use strict';
/* istanbul ignore if */
if (typeof define === 'function' && /* istanbul ignore next */ define.amd) {
define(['angular', 'moment'], factory); // AMD
} else {
factory(window.angular, window.moment); // Browser global
}
}(function (angular, moment) {
'use strict';
angular.module('ui.bootstrap.datetimepicker', [])
.constant('dateTimePickerConfig', {
dropdownSelector: null,
minuteStep: 5,
minView: 'minute',
startView: 'day'
})
.directive('datetimepicker', ['$log', 'dateTimePickerConfig', function datetimepickerDirective($log, defaultConfig) {
function DateObject() {
this.dateValue = new Date().getTime();
this.selectable = true;
var validProperties = ['dateValue', 'display', 'active', 'selectable', 'past', 'future'];
for (var prop in arguments[0]) {
/* istanbul ignore else */
//noinspection JSUnfilteredForInLoop
if (validProperties.indexOf(prop) >= 0) {
//noinspection JSUnfilteredForInLoop
this[prop] = arguments[0][prop];
}
}
}
var validateConfiguration = function validateConfiguration(configuration) {
var validOptions = ['startView', 'minView', 'minuteStep', 'dropdownSelector'];
for (var prop in configuration) {
//noinspection JSUnfilteredForInLoop
if (validOptions.indexOf(prop) < 0) {
throw ('invalid option: ' + prop);
}
}
// Order of the elements in the validViews array is significant.
var validViews = ['minute', 'hour', 'day', 'month', 'year'];
if (validViews.indexOf(configuration.startView) < 0) {
throw ('invalid startView value: ' + configuration.startView);
}
if (validViews.indexOf(configuration.minView) < 0) {
throw ('invalid minView value: ' + configuration.minView);
}
if (validViews.indexOf(configuration.minView) > validViews.indexOf(configuration.startView)) {
throw ('startView must be greater than minView');
}
if (!angular.isNumber(configuration.minuteStep)) {
throw ('minuteStep must be numeric');
}
if (configuration.minuteStep <= 0 || configuration.minuteStep >= 60) {
throw ('minuteStep must be greater than zero and less than 60');
}
if (configuration.dropdownSelector !== null && !angular.isString(configuration.dropdownSelector)) {
throw ('dropdownSelector must be a string');
}
/* istanbul ignore next */
if (configuration.dropdownSelector !== null && ((typeof jQuery === 'undefined') || (typeof jQuery().dropdown !== 'function'))) {
$log.error('Please DO NOT specify the dropdownSelector option unless you are using jQuery AND Bootstrap.js. ' +
'Please include jQuery AND Bootstrap.js, or write code to close the dropdown in the on-set-time callback. \n\n' +
'The dropdownSelector configuration option is being removed because it will not function properly.');
delete configuration.dropdownSelector;
}
};
return {
restrict: 'E',
require: 'ngModel',
template: '<div class="datetimepicker table-responsive">' +
'<table class="table table-striped">' +
' <thead>' +
' <tr>' +
' <th class="left" data-ng-click="changeView(data.currentView, data.leftDate, $event)" data-ng-show="data.leftDate.selectable"><i class="glyphicon glyphicon-arrow-left"/></th>' +
' <th class="switch" colspan="5" data-ng-show="data.previousViewDate.selectable" data-ng-click="changeView(data.previousView, data.previousViewDate, $event)">{{ data.previousViewDate.display }}</th>' +
' <th class="right" data-ng-click="changeView(data.currentView, data.rightDate, $event)" data-ng-show="data.rightDate.selectable"><i class="glyphicon glyphicon-arrow-right"/></th>' +
' </tr>' +
' <tr>' +
' <th class="dow" data-ng-repeat="day in data.dayNames" >{{ day }}</th>' +
' </tr>' +
' </thead>' +
' <tbody>' +
' <tr data-ng-if="data.currentView !== \'day\'" >' +
' <td colspan="7" >' +
' <span class="{{ data.currentView }}" ' +
' data-ng-repeat="dateObject in data.dates" ' +
' data-ng-class="{active: dateObject.active, past: dateObject.past, future: dateObject.future, disabled: !dateObject.selectable}" ' +
' data-ng-click="changeView(data.nextView, dateObject, $event)">{{ dateObject.display }}</span> ' +
' </td>' +
' </tr>' +
' <tr data-ng-if="data.currentView === \'day\'" data-ng-repeat="week in data.weeks">' +
' <td data-ng-repeat="dateObject in week.dates" ' +
' data-ng-click="changeView(data.nextView, dateObject, $event)"' +
' class="day" ' +
' data-ng-class="{active: dateObject.active, past: dateObject.past, future: dateObject.future, disabled: !dateObject.selectable}" >{{ dateObject.display }}</td>' +
' </tr>' +
' </tbody>' +
'</table></div>',
scope: {
onSetTime: '&',
beforeRender: '&'
},
replace: true,
link: function link(scope, element, attrs, ngModelController) {
var directiveConfig = {};
if (attrs.datetimepickerConfig) {
directiveConfig = scope.$parent.$eval(attrs.datetimepickerConfig);
}
var configuration = {};
angular.extend(configuration, defaultConfig, directiveConfig);
validateConfiguration(configuration);
var startOfDecade = function startOfDecade(unixDate) {
var startYear = (parseInt(moment.utc(unixDate).year() / 10, 10) * 10);
return moment.utc(unixDate).year(startYear).startOf('year');
};
var dataFactory = {
year: function year(unixDate) {
var selectedDate = moment.utc(unixDate).startOf('year');
// View starts one year before the decade starts and ends one year after the decade ends
// i.e. passing in a date of 1/1/2013 will give a range of 2009 to 2020
// Truncate the last digit from the current year and subtract 1 to get the start of the decade
var startDecade = (parseInt(selectedDate.year() / 10, 10) * 10);
var startDate = moment.utc(startOfDecade(unixDate)).subtract(1, 'year').startOf('year');
var activeYear = ngModelController.$modelValue ? moment(ngModelController.$modelValue).year() : 0;
var result = {
'currentView': 'year',
'nextView': configuration.minView === 'year' ? 'setTime' : 'month',
'previousViewDate': new DateObject({dateValue: null, display: startDecade + '-' + (startDecade + 9)}),
'leftDate': new DateObject({dateValue: moment.utc(startDate).subtract(9, 'year').valueOf()}),
'rightDate': new DateObject({dateValue: moment.utc(startDate).add(11, 'year').valueOf()}),
'dates': []
};
for (var i = 0; i < 12; i += 1) {
var yearMoment = moment.utc(startDate).add(i, 'years');
var dateValue = {
'dateValue': yearMoment.valueOf(),
'display': yearMoment.format('YYYY'),
'past': yearMoment.year() < startDecade,
'future': yearMoment.year() > startDecade + 9,
'active': yearMoment.year() === activeYear
};
result.dates.push(new DateObject(dateValue));
}
return result;
},
month: function month(unixDate) {
var startDate = moment.utc(unixDate).startOf('year');
var previousViewDate = startOfDecade(unixDate);
var activeDate = ngModelController.$modelValue ? moment(ngModelController.$modelValue).format('YYYY-MMM') : 0;
var result = {
'previousView': 'year',
'currentView': 'month',
'nextView': configuration.minView === 'month' ? 'setTime' : 'day',
'previousViewDate': new DateObject({
dateValue: previousViewDate.valueOf(),
display: startDate.format('YYYY')
}),
'leftDate': new DateObject({dateValue: moment.utc(startDate).subtract(1, 'year').valueOf()}),
'rightDate': new DateObject({dateValue: moment.utc(startDate).add(1, 'year').valueOf()}),
'dates': []
};
for (var i = 0; i < 12; i += 1) {
var monthMoment = moment.utc(startDate).add(i, 'months');
var dateValue = {
'dateValue': monthMoment.valueOf(),
'display': monthMoment.format('MMM'),
'active': monthMoment.format('YYYY-MMM') === activeDate
};
result.dates.push(new DateObject(dateValue));
}
return result;
},
day: function day(unixDate) {
var selectedDate = moment.utc(unixDate);
var startOfMonth = moment.utc(selectedDate).startOf('month');
var previousViewDate = moment.utc(selectedDate).startOf('year');
var endOfMonth = moment.utc(selectedDate).endOf('month');
var startDate = moment.utc(startOfMonth).subtract(Math.abs(startOfMonth.weekday()), 'days');
var activeDate = ngModelController.$modelValue ? moment(ngModelController.$modelValue).format('YYYY-MMM-DD') : '';
var result = {
'previousView': 'month',
'currentView': 'day',
'nextView': configuration.minView === 'day' ? 'setTime' : 'hour',
'previousViewDate': new DateObject({
dateValue: previousViewDate.valueOf(),
display: startOfMonth.format('YYYY-MMM')
}),
'leftDate': new DateObject({dateValue: moment.utc(startOfMonth).subtract(1, 'months').valueOf()}),
'rightDate': new DateObject({dateValue: moment.utc(startOfMonth).add(1, 'months').valueOf()}),
'dayNames': [],
'weeks': []
};
for (var dayNumber = 0; dayNumber < 7; dayNumber += 1) {
result.dayNames.push(moment.utc().weekday(dayNumber).format('dd'));
}
for (var i = 0; i < 6; i += 1) {
var week = {dates: []};
for (var j = 0; j < 7; j += 1) {
var monthMoment = moment.utc(startDate).add((i * 7) + j, 'days');
var dateValue = {
'dateValue': monthMoment.valueOf(),
'display': monthMoment.format('D'),
'active': monthMoment.format('YYYY-MMM-DD') === activeDate,
'past': monthMoment.isBefore(startOfMonth),
'future': monthMoment.isAfter(endOfMonth)
};
week.dates.push(new DateObject(dateValue));
}
result.weeks.push(week);
}
return result;
},
hour: function hour(unixDate) {
var selectedDate = moment.utc(unixDate).startOf('day');
var previousViewDate = moment.utc(selectedDate).startOf('month');
var activeFormat = ngModelController.$modelValue ? moment(ngModelController.$modelValue).format('YYYY-MM-DD H') : '';
var result = {
'previousView': 'day',
'currentView': 'hour',
'nextView': configuration.minView === 'hour' ? 'setTime' : 'minute',
'previousViewDate': new DateObject({
dateValue: previousViewDate.valueOf(),
display: selectedDate.format('ll')
}),
'leftDate': new DateObject({dateValue: moment.utc(selectedDate).subtract(1, 'days').valueOf()}),
'rightDate': new DateObject({dateValue: moment.utc(selectedDate).add(1, 'days').valueOf()}),
'dates': []
};
for (var i = 0; i < 24; i += 1) {
var hourMoment = moment.utc(selectedDate).add(i, 'hours');
var dateValue = {
'dateValue': hourMoment.valueOf(),
'display': hourMoment.format('LT'),
'active': hourMoment.format('YYYY-MM-DD H') === activeFormat
};
result.dates.push(new DateObject(dateValue));
}
return result;
},
minute: function minute(unixDate) {
var selectedDate = moment.utc(unixDate).startOf('hour');
var previousViewDate = moment.utc(selectedDate).startOf('day');
var activeFormat = ngModelController.$modelValue ? moment(ngModelController.$modelValue).format('YYYY-MM-DD H:mm') : '';
var result = {
'previousView': 'hour',
'currentView': 'minute',
'nextView': 'setTime',
'previousViewDate': new DateObject({
dateValue: previousViewDate.valueOf(),
display: selectedDate.format('lll')
}),
'leftDate': new DateObject({dateValue: moment.utc(selectedDate).subtract(1, 'hours').valueOf()}),
'rightDate': new DateObject({dateValue: moment.utc(selectedDate).add(1, 'hours').valueOf()}),
'dates': []
};
var limit = 60 / configuration.minuteStep;
for (var i = 0; i < limit; i += 1) {
var hourMoment = moment.utc(selectedDate).add(i * configuration.minuteStep, 'minute');
var dateValue = {
'dateValue': hourMoment.valueOf(),
'display': hourMoment.format('LT'),
'active': hourMoment.format('YYYY-MM-DD H:mm') === activeFormat
};
result.dates.push(new DateObject(dateValue));
}
return result;
},
setTime: function setTime(unixDate) {
var tempDate = new Date(unixDate);
var newDate = new Date(tempDate.getTime() + (tempDate.getTimezoneOffset() * 60000));
var oldDate = ngModelController.$modelValue;
ngModelController.$setViewValue(newDate);
if (configuration.dropdownSelector) {
jQuery(configuration.dropdownSelector).dropdown('toggle');
}
scope.onSetTime({newDate: newDate, oldDate: oldDate});
return dataFactory[configuration.startView](unixDate);
}
};
var getUTCTime = function getUTCTime(modelValue) {
var tempDate = (modelValue ? moment(modelValue).toDate() : new Date());
return tempDate.getTime() - (tempDate.getTimezoneOffset() * 60000);
};
scope.changeView = function changeView(viewName, dateObject, event) {
if (event) {
event.stopPropagation();
event.preventDefault();
}
if (viewName && (dateObject.dateValue > -Infinity) && dateObject.selectable && dataFactory[viewName]) {
var result = dataFactory[viewName](dateObject.dateValue);
var weekDates = [];
if (result.weeks) {
for (var i = 0; i < result.weeks.length; i += 1) {
var week = result.weeks[i];
for (var j = 0; j < week.dates.length; j += 1) {
var weekDate = week.dates[j];
weekDates.push(weekDate);
}
}
}
scope.beforeRender({
$view: result.currentView,
$dates: result.dates || weekDates,
$leftDate: result.leftDate,
$upDate: result.previousViewDate,
$rightDate: result.rightDate
});
scope.data = result;
}
};
ngModelController.$render = function $render() {
scope.changeView(configuration.startView, new DateObject({dateValue: getUTCTime(ngModelController.$viewValue)}));
};
}
};
}]);
}));
/*globals angular, moment, jQuery */
/*jslint vars:true */
/**
* @license angular-date-time-input v0.1.0
* (c) 2013 Knight Rider Consulting, Inc. http://www.knightrider.com
* License: MIT
*/
/**
*
* @author Dale "Ducky" Lotts
* @since 2013-Sep-23
*/
angular.module('ui.dateTimeInput', []).directive('dateTimeInput',
[
function () {
"use strict";
return {
require: 'ngModel',
restrict: 'A',
link: function (scope, element, attrs, controller) {
if (!attrs.dateTimeInput) {
throw ("dateTimeInput must specify a date format");
}
var validateFn = function (viewValue) {
var result = viewValue;
if (viewValue) {
var momentValue = moment(viewValue);
if (momentValue.isValid()) {
controller.$setValidity(attrs.ngModel, true);
result = momentValue.format();
} else {
controller.$setValidity(attrs.ngModel, false);
}
}
return result;
};
var formatFn = function (modelValue) {
var result = modelValue;
if (modelValue && moment(modelValue).isValid()) {
result = moment(modelValue).format(attrs.dateTimeInput);
}
return result;
};
controller.$parsers.unshift(validateFn);
controller.$formatters.push(formatFn);
element.bind('blur', function () {
var viewValue = controller.$modelValue;
angular.forEach(controller.$formatters, function (formatter) {
viewValue = formatter(viewValue);
});
controller.$viewValue = viewValue;
controller.$render();
});
}
};
}
]);
/*
* angular-auto-validate - v1.10.21 - 2014-11-13
* https://github.com/jonsamwell/angular-auto-validate
* Copyright (c) 2014 Jon Samwell (http://www.jonsamwell.com)
*/
(function (angular) {
'use strict';
angular.module('jcs-autoValidate', []);
}(angular));
(function (angular) {
'use strict';
angular.module('jcs-autoValidate')
.provider('validator', [
function () {
var elementStateModifiers = {},
enableValidElementStyling = true,
enableInvalidElementStyling = true,
validationEnabled = true,
toBoolean = function (value) {
var v;
if (value && value.length !== 0) {
v = value.toLowerCase();
value = !(v === 'f' || v === '0' || v === 'false');
} else {
value = false;
}
return value;
},
getAttributeValue = function (el, attrName) {
var val;
if (el !== undefined) {
val = el.attr(attrName) || el.attr('data-' + attrName);
}
return val;
},
getBooleanAttributeValue = function (el, attrName) {
return toBoolean(getAttributeValue(el, attrName));
},
validElementStylingEnabled = function (el) {
return enableValidElementStyling && !getBooleanAttributeValue(el, 'disable-valid-styling');
},
invalidElementStylingEnabled = function (el) {
return enableInvalidElementStyling && !getBooleanAttributeValue(el, 'disable-invalid-styling');
};
/**
* @ngdoc function
* @name validator#enable
* @methodOf validator
*
* @description
* By default auto validate will validate all forms and elements with an ngModel directive on. By
* setting enabled to false you will explicitly have to opt in to enable validation on forms and child
* elements.
*
* Note: this can be overridden by add the 'auto-validate-enabled="true/false' attribute to a form.
*
* Example:
* <pre>
* app.config(function (validator) {
* validator.enable(false);
* });
* </pre>
*
* @param {Boolean} isEnabled true to enable, false to disable.
*/
this.enable = function (isEnabled) {
validationEnabled = isEnabled;
};
/**
* @ngdoc function
* @name validator#isEnabled
* @methodOf validator
*
* @description
* Returns true if the library is enabeld.
*
* @return {Boolean} true if enabled, otherwise false.
*/
this.isEnabled = function () {
return validationEnabled;
};
/**
* @ngdoc function
* @name validator#setDefaultElementModifier
* @methodOf validator
*
* @description
* Sets the default element modifier that will be used by the validator
* to change an elements UI state. Please ensure the modifier has been registered
* before setting it as default.
*
* Note: this can be changed by setting the
* element modifier attribute on the input element 'data-element-modifier="myCustomModifier"'
*
* Example:
* <pre>
* app.config(function (validator) {
* validator.setDefaultElementModifier('myCustomModifier');
* });
* </pre>
*
* @param {string} key The key name of the modifier.
*/
this.setDefaultElementModifier = function (key) {
if (elementStateModifiers[key] === undefined) {
throw new Error('Element modifier not registered: ' + key);
}
this.defaultElementModifier = key;
};
/**
* @ngdoc function
* @name validator#registerDomModifier
* @methodOf validator
*
* @description
* Registers an object that adheres to the elementModifier interface and is
* able to modifier an elements dom so that appears valid / invalid for a specific
* scenario i.e. the Twitter Bootstrap css framework, Foundation CSS framework etc.
*
* Example:
* <pre>
* app.config(function (validator) {
* validator.registerDomModifier('customDomModifier', {
* makeValid: function (el) {
* el.removeClass(el, 'invalid');
* el.addClass(el, 'valid');
* },
* makeInvalid: function (el, err, domManipulator) {
* el.removeClass(el, 'valid');
* el.addClass(el, 'invalid');
* }
* });
* });
* </pre>
*
* @param {string} key The key name of the modifier
* @param {object} modifier An object which implements the elementModifier interface
*/
this.registerDomModifier = function (key, modifier) {
elementStateModifiers[key] = modifier;
};
/**
* @ngdoc function
* @name validator#setErrorMessageResolver
* @methodOf validator
*
* @description
* Registers an object that adheres to the elementModifier interface and is
* able to modifier an elements dom so that appears valid / invalid for a specific
* scenario i.e. the Twitter Bootstrap css framework, Foundation CSS framework etc.
*
* Example:
* <pre>
* app.config(function (validator) {
* validator.setErrorMessageResolver(function (errorKey, el) {
* var defer = $q.defer();
* // resolve the correct error from the given key and resolve the returned promise.
* return defer.promise();
* });
* });
* </pre>
*
* @param {function} resolver A method that returns a promise with the resolved error message in.
*/
this.setErrorMessageResolver = function (resolver) {
this.errorMessageResolver = resolver;
};
/**
* @ngdoc function
* @name validator#getErrorMessage
* @methodOf validator
*
* @description
* Resolves the error message for the given error type.
*
* @param {String} errorKey The error type.
* @param {Element} el The UI element that is the focus of the error.
* It is provided as the error message may need information from the element i.e. ng-min (the min allowed value).
*/
this.getErrorMessage = function (errorKey, el) {
if (this.errorMessageResolver === undefined) {
throw new Error('Please set an error message resolver via the setErrorMessageResolver function before attempting to resolve an error message.');
}
return this.errorMessageResolver(errorKey, el);
};
/**
* @ngdoc function
* @name validator#setValidElementStyling
* @methodOf validator
*
* @description
* Globally enables valid element visual styling. This is enabled by default.
*
* @param {Boolean} enabled True to enable style otherwise false.
*/
this.setValidElementStyling = function (enabled) {
enableValidElementStyling = enabled;
};
/**
* @ngdoc function
* @name validator#setInvalidElementStyling
* @methodOf validator
*
* @description
* Globally enables invalid element visual styling. This is enabled by default.
*
* @param {Boolean} enabled True to enable style otherwise false.
*/
this.setInvalidElementStyling = function (enabled) {
enableInvalidElementStyling = enabled;
};
this.getDomModifier = function (el) {
var modifierKey = (el !== undefined ? el.attr('element-modifier') : this.defaultElementModifier) ||
(el !== undefined ? el.attr('data-element-modifier') : this.defaultElementModifier) ||
this.defaultElementModifier;
if (modifierKey === undefined) {
throw new Error('Please set a default dom modifier via the setDefaultElementModifier method on the validator class.');
}
return elementStateModifiers[modifierKey];
};
this.makeValid = function (el) {
if (validElementStylingEnabled(el)) {
this.getDomModifier(el).makeValid(el);
} else {
this.makeDefault(el);
}
};
this.makeInvalid = function (el, errorMsg) {
if (invalidElementStylingEnabled(el)) {
this.getDomModifier(el).makeInvalid(el, errorMsg);
} else {
this.makeDefault(el);
}
};
this.makeDefault = function (el) {
var dm = this.getDomModifier(el);
if (dm.makeDefault) {
dm.makeDefault(el);
}
};
this.$get = [
function () {
return this;
}
];
}
]);
}(angular));
(function (angular) {
'use strict';
angular.module('jcs-autoValidate')
.factory('bootstrap3ElementModifier', [
function () {
var reset = function (el) {
angular.forEach(el.find('span'), function (spanEl) {
spanEl = angular.element(spanEl);
if (spanEl.hasClass('error-msg') || spanEl.hasClass('form-control-feedback') || spanEl.hasClass('control-feedback')) {
spanEl.remove();
}
});
el.removeClass('has-success has-error has-feedback');
},
findWithClassElementAsc = function (el, klass) {
var parent = el;
for (var i = 0; i <= 3; i += 1) {
if (parent !== undefined && parent.hasClass(klass)) {
break;
} else if (parent !== undefined) {
parent = parent.parent();
}
}
return parent;
},
findWithClassElementDesc = function (el, klass) {
var child;
for (var i = 0; i < el.children.length; i += 1) {
child = el.children[i];
if (child !== undefined && angular.element(child).hasClass(klass)) {
break;
} else if (child.children !== undefined) {
child = findWithClassElementDesc(child, klass);
if (child.length > 0) {
break;
}
}
}
return angular.element(child);
},
findFormGroupElement = function (el) {
return findWithClassElementAsc(el, 'form-group');
},
findInputGroupElement = function (el) {
return findWithClassElementDesc(el, 'input-group');
},
insertAfter = function (referenceNode, newNode) {
referenceNode[0].parentNode.insertBefore(newNode[0], referenceNode[0].nextSibling);
},
/**
* @ngdoc property
* @name bootstrap3ElementModifier#addValidationStateIcons
* @propertyOf bootstrap3ElementModifier
* @returns {bool} True if an state icon will be added to the element in the valid and invalid control
* states. The default is false.
*/
addValidationStateIcons = false,
/**
* @ngdoc function
* @name bootstrap3ElementModifier#enableValidationStateIcons
* @methodOf bootstrap3ElementModifier
*
* @description
* Makes an element appear invalid by apply an icon to the input element.
*
* @param {bool} enable - True to enable the icon otherwise false.
*/
enableValidationStateIcons = function (enable) {
addValidationStateIcons = enable;
},
/**
* @ngdoc function
* @name bootstrap3ElementModifier#makeValid
* @methodOf bootstrap3ElementModifier
*
* @description
* Makes an element appear valid by apply bootstrap 3 specific styles and child elements. If the service
* property 'addValidationStateIcons' is true it will also append validation glyphicon to the element.
* See: http://getbootstrap.com/css/#forms-control-validation
*
* @param {Element} el - The input control element that is the target of the validation.
*/
makeValid = function (el) {
var frmGroupEl = findFormGroupElement(el),
inputGroupEl = findInputGroupElement(frmGroupEl[0]);
reset(frmGroupEl);
frmGroupEl.addClass('has-success ' + (inputGroupEl.length > 0 ? '' : 'has-feedback'));
if (addValidationStateIcons) {
var iconElText = '<span class="glyphicon glyphicon-ok form-control-feedback"></span>';
if (inputGroupEl.length > 0) {
iconElText = iconElText.replace('form-', '');
iconElText = '<span class="input-group-addon control-feedback">' + iconElText + '</span';
}
insertAfter(el, angular.element(iconElText));
}
},
/**
* @ngdoc function
* @name bootstrap3ElementModifier#makeInvalid
* @methodOf bootstrap3ElementModifier
*
* @description
* Makes an element appear invalid by apply bootstrap 3 specific styles and child elements. If the service
* property 'addValidationStateIcons' is true it will also append validation glyphicon to the element.
* See: http://getbootstrap.com/css/#forms-control-validation
*
* @param {Element} el - The input control element that is the target of the validation.
*/
makeInvalid = function (el, errorMsg) {
var frmGroupEl = findFormGroupElement(el),
inputGroupEl = findInputGroupElement(frmGroupEl[0]),
helpTextEl = angular.element('<span class="help-block has-error error-msg">' + errorMsg + '</span>');
reset(frmGroupEl, inputGroupEl);
frmGroupEl.addClass('has-error ' + (inputGroupEl.length > 0 ? '' : 'has-feedback'));
insertAfter(inputGroupEl.length > 0 ? inputGroupEl : el, helpTextEl);
if (addValidationStateIcons) {
var iconElText = '<span class="glyphicon glyphicon-remove form-control-feedback"></span>';
if (inputGroupEl.length > 0) {
iconElText = iconElText.replace('form-', '');
iconElText = '<span class="input-group-addon control-feedback">' + iconElText + '</span';
}
insertAfter(el, angular.element(iconElText));
}
},
/**
* @ngdoc function
* @name bootstrap3ElementModifier#makeDefault
* @methodOf bootstrap3ElementModifier
*
* @description
* Makes an element appear in its default visual state by apply bootstrap 3 specific styles and child elements.
*
* @param {Element} el - The input control element that is the target of the validation.
*/
makeDefault = function (el) {
var frmGroupEl = findFormGroupElement(el);
reset(frmGroupEl);
};
return {
makeValid: makeValid,
makeInvalid: makeInvalid,
makeDefault: makeDefault,
enableValidationStateIcons: enableValidationStateIcons,
key: 'bs3'
};
}
]);
}(angular));
(function (angular) {
'use strict';
/*
* Taken from https://github.com/angular/angular.js/issues/2690#issue-14462164 (with added tests of course!)
*/
angular.module('jcs-autoValidate').factory('jcs-debounce', [
'$timeout',
function ($timeout) {
var debounce = function (fn, timeout, apply) {
timeout = angular.isUndefined(timeout) ? 0 : timeout;
apply = angular.isUndefined(apply) ? true : apply; // !!default is true! most suitable to my experience
var nthCall = 0;
return function () { // intercepting fn
var that = this;
var argz = arguments;
nthCall += 1;
var later = (function (version) {
return function () {
if (version === nthCall) {
return fn.apply(that, argz);
}
};
})(nthCall);
return $timeout(later, timeout, apply);
};
};
return {
debounce: debounce
};
}
]);
}(angular));
(function (String, angular) {
'use strict';
/**
* Replaces string placeholders with corresponding template string
*/
if (!('format' in String.prototype)) {
String.prototype.format = function () {
var args = arguments;
return this.replace(/{(\d+)}/g, function (match, number) {
return typeof args[number] !== undefined ? args[number] : match;
});
};
}
angular.autoValidate = angular.autoValidate || {
errorMessages: {}
};
angular.autoValidate.errorMessages['en-us'] = angular.autoValidate.errorMessages['en-gb'] = {
defaultMsg: 'Please add error message for {0}',
email: 'Please enter a valid email address',
minlength: 'Please enter at least {0} characters',
maxlength: 'You have entered more than the maximum {0} characters',
min: 'Please enter the minimum number of {0}',
max: 'Please enter the maximum number of {0}',
required: 'This field is required',
date: 'Please enter a valid date',
pattern: 'Please ensure the entered information adheres to this pattern {0}',
number: 'Please enter a valid number',
url: 'Please enter a valid URL in the format of http(s)://wwww.google.com'
};
angular.module('jcs-autoValidate')
.factory('defaultErrorMessageResolver', [
'$q',
'$http',
function ($q, $http) {
var currentCulture = 'en-gb',
i18nFileRootPath = 'js/angular-auto-validate/dist/lang',
cultureRetrievalPromise,
loadRemoteCulture = function (culture) {
cultureRetrievalPromise = $http.get('{0}/jcs-auto-validate_{1}.json'.format(i18nFileRootPath, culture.toLowerCase()));
return cultureRetrievalPromise;
},
/**
* @ngdoc function
* @name defaultErrorMessageResolver#setI18nFileRootPath
* @methodOf defaultErrorMessageResolver
*
* @description
* Set the root path to the il8n files on the server
*
* @param {String} rootPath - The root path on the server to the il8n file - this defaults
* to 'js/angular-auto-validate/lang/'
*/
setI18nFileRootPath = function (rootPath) {
i18nFileRootPath = rootPath;
},
/**
* @ngdoc function
* @name defaultErrorMessageResolver#setCulture
* @methodOf defaultErrorMessageResolver
*
* @description
* Set the culture for the error messages by loading an the correct culture resource file.
*
* @param {String} culture - The new culture in the format of 'en-gb' etc.
* @param {Function} cultureLoadingFn - A optional function to load the culture resolve which should
* return a promise which is resolved with the culture errorMessage object. If a function is not specified
* the culture file is loaded from the **i18nFileRootPath**.
* @returns {Promise} - A promise which is resolved with the loaded culture error messages object.
*/
setCulture = function (culture, cultureLoadingFn) {
var defer = $q.defer();
cultureLoadingFn = cultureLoadingFn || loadRemoteCulture;
currentCulture = culture.toLowerCase();
if (angular.autoValidate.errorMessages[currentCulture] === undefined) {
cultureRetrievalPromise = cultureLoadingFn(culture);
cultureRetrievalPromise.then(function (response) {
cultureRetrievalPromise = undefined;
angular.autoValidate.errorMessages[currentCulture] = response.data;
defer.resolve(angular.autoValidate.errorMessages[currentCulture]);
}, function (err) {
angular.autoValidate.errorMessages[currentCulture] = {
defaultMsg: 'Loading culture failed!'
};
cultureRetrievalPromise = null;
defer.reject(err);
});
} else {
defer.resolve(angular.autoValidate.errorMessages[currentCulture]);
}
return defer.promise;
},
getErrorMessages = function (culture) {
var defer = $q.defer();
culture = culture === undefined ? currentCulture : culture.toLowerCase();
if (cultureRetrievalPromise !== undefined) {
cultureRetrievalPromise.then(function () {
defer.resolve(angular.autoValidate.errorMessages[culture]);
}, function (err) {
defer.reject(err);
});
} else {
defer.resolve(angular.autoValidate.errorMessages[culture]);
}
return defer.promise;
},
getMessageTypeOverride = function (errorType, el) {
var overrideKey;
if (el) {
// try and find an attribute which overrides the given error type in the form of errorType-err-type="someMsgKey"
errorType += '-err-type';
overrideKey = el.attr(errorType);
if (overrideKey === undefined) {
overrideKey = el.attr('data-ng-' + errorType) || el.attr('ng-' + errorType);
}
if (overrideKey) {
overrideKey = overrideKey.replace(/[\W]/g, '');
}
}
return overrideKey;
},
/**
* @ngdoc function
* @name defaultErrorMessageResolver#resolve
* @methodOf defaultErrorMessageResolver
*
* @description
* Resolves a validate error type into a user validation error message
*
* @param {String} errorType - The type of validation error that has occurred.
* @param {Element} el - The input element that is the source of the validation error.
* @returns {Promise} A promise that is resolved when the validation message has been produced.
*/
resolve = function (errorType, el) {
var defer = $q.defer(),
errMsg,
parameters = [],
parameter,
messageTypeOverride;
if (cultureRetrievalPromise !== undefined) {
cultureRetrievalPromise.then(function () {
resolve(errorType, el).then(function (msg) {
defer.resolve(msg);
});
});
} else {
errMsg = angular.autoValidate.errorMessages[currentCulture][errorType];
messageTypeOverride = getMessageTypeOverride(errorType, el);
if (messageTypeOverride) {
errMsg = angular.autoValidate.errorMessages[currentCulture][messageTypeOverride];
}
if (errMsg === undefined) {
errMsg = angular.autoValidate.errorMessages[currentCulture].defaultMsg.format(errorType);
}
if (el && el.attr) {
try {
parameter = el.attr(errorType);
if (parameter === undefined) {
parameter = el.attr('data-ng-' + errorType) || el.attr('ng-' + errorType);
}
parameters.push(parameter || '');
errMsg = errMsg.format(parameters);
} catch (e) {}
}
defer.resolve(errMsg);
}
return defer.promise;
};
return {
setI18nFileRootPath: setI18nFileRootPath,
setCulture: setCulture,
getErrorMessages: getErrorMessages,
resolve: resolve
};
}
]);
}(String, angular));
(function (angular) {
'use strict';
angular.module('jcs-autoValidate')
.factory('foundation5ElementModifier', [
function () {
var reset = function (el, inputEl) {
angular.forEach(el.find('small'), function (smallEl) {
if (angular.element(smallEl).hasClass('error')) {
angular.element(smallEl).remove();
}
});
inputEl.removeClass('error');
},
findParentColumn = function (el) {
var parent = el;
for (var i = 0; i <= 3; i += 1) {
if (parent !== undefined && parent.hasClass('columns')) {
break;
} else if (parent !== undefined) {
parent = parent.parent();
}
}
return parent;
},
/**
* @ngdoc function
* @name foundation5ElementModifier#makeValid
* @methodOf foundation5ElementModifier
*
* @description
* Makes an element appear valid by apply Foundation 5 specific styles and child elements.
* See: http://foundation.zurb.com/docs/components/forms.html
*
* @param {Element} el - The input control element that is the target of the validation.
*/
makeValid = function (el) {
var parentColumn = findParentColumn(el);
reset(parentColumn && parentColumn.length > 0 ? parentColumn : el, el);
},
/**
* @ngdoc function
* @name foundation5ElementModifier#makeInvalid
* @methodOf foundation5ElementModifier
*
* @description
* Makes an element appear invalid by apply Foundation 5 specific styles and child elements.
* See: http://foundation.zurb.com/docs/components/forms.html
*
* @param {Element} el - The input control element that is the target of the validation.
*/
makeInvalid = function (el, errorMsg) {
var parentColumn = findParentColumn(el),
helpTextEl;
reset(parentColumn || el, el);
el.addClass('error');
if (parentColumn) {
helpTextEl = angular.element('<small class="error">' + errorMsg + '</small>');
parentColumn.append(helpTextEl);
}
},
/**
* @ngdoc function
* @name foundation5ElementModifier#makeDefault
* @methodOf foundation5ElementModifier
*
* @description
* Makes an element appear in its default visual state by apply foundation 5 specific styles and child elements.
*
* @param {Element} el - The input control element that is the target of the validation.
*/
makeDefault = function (el) {
makeValid(el);
};
return {
makeValid: makeValid,
makeInvalid: makeInvalid,
makeDefault: makeDefault,
key: 'foundation5'
};
}
]);
}(angular));
(function (angular) {
'use strict';
angular.module('jcs-autoValidate')
.factory('jcs-elementUtils', [
function () {
var isElementVisible = function (el) {
return el[0].offsetWidth > 0 && el[0].offsetHeight > 0;
};
return {
isElementVisible: isElementVisible
};
}
]);
angular.module('jcs-autoValidate')
.factory('validationManager', [
'validator',
'jcs-elementUtils',
function (validator, elementUtils) {
var elementTypesToValidate = ['input', 'textarea', 'select', 'form'],
elementIsVisible = function (el) {
return elementUtils.isElementVisible(el);
},
shouldValidateElement = function (el) {
return el && el.length > 0 && elementIsVisible(el) && elementTypesToValidate.indexOf(el[0].nodeName.toLowerCase()) > -1;
},
/**
* @ngdoc validateElement
* @name validation#validateElement
* @param {object} modelCtrl holds the information about the element e.g. $invalid, $valid
* @param {Boolean} forceValidation if set to true forces the validation even if the element is pristine
* @description
* Validate the form element and make invalid/valid element model status.
*/
validateElement = function (modelCtrl, el, forceValidation) {
var isValid = true,
needsValidation = modelCtrl.$pristine === false || forceValidation,
errorType,
findErrorType = function ($errors) {
var keepGoing = true,
errorTypeToReturn;
angular.forEach($errors, function (status, errortype) {
if (keepGoing && status) {
keepGoing = false;
errorTypeToReturn = errortype;
}
});
return errorTypeToReturn;
};
if ((forceValidation || (shouldValidateElement(el) && modelCtrl && needsValidation))) {
isValid = !modelCtrl.$invalid;
if (isValid) {
validator.makeValid(el);
} else {
errorType = findErrorType(modelCtrl.$error);
if (errorType === undefined) {
// we have a weird situation some users are encountering where a custom control
// is valid but the ngModel is report it isn't and thus no valid error type can be found
isValid = true;
} else {
validator.getErrorMessage(errorType, el).then(function (errorMsg) {
validator.makeInvalid(el, errorMsg);
});
}
}
}
return isValid;
},
resetElement = function (element) {
validator.makeDefault(element);
},
resetForm = function (frmElement) {
angular.forEach(frmElement[0], function (element) {
var controller,
ctrlElement = angular.element(element);
controller = ctrlElement.controller('ngModel');
if (controller !== undefined) {
if (ctrlElement[0].nodeName.toLowerCase() === 'form') {
// we probably have a sub form
resetForm(ctrlElement);
} else {
controller.$setPristine();
}
}
});
},
validateForm = function (frmElement) {
var frmValid = true,
frmCtrl = frmElement ? angular.element(frmElement).controller('form') : undefined,
processElement = function (ctrlElement, force) {
var controller, isValid;
ctrlElement = angular.element(ctrlElement);
controller = ctrlElement.controller('ngModel');
if (controller !== undefined && (force || shouldValidateElement(ctrlElement))) {
if (ctrlElement[0].nodeName.toLowerCase() === 'form') {
// we probably have a sub form
validateForm(ctrlElement);
} else {
isValid = validateElement(controller, ctrlElement, true);
frmValid = frmValid && isValid;
}
}
};
if (frmElement === undefined || (frmCtrl !== undefined && frmCtrl.disableDynamicValidation)) {
return frmElement !== undefined;
}
angular.forEach(frmElement[0], function (ctrlElement) {
processElement(ctrlElement);
});
// If you have a custom form control that should be validated i.e.
// <my-custom-element>...</my-custom-element> it will not be part of the forms
// HTMLFormControlsCollection and thus won't be included in the above element iteration although
// it will be on the Angular FormController (if it has a name attribute). So adding the directive
// register-custom-form-control="" to the control root and autoValidate will include it in this
// iteration.
if (frmElement[0].customHTMLFormControlsCollection) {
angular.forEach(frmElement[0].customHTMLFormControlsCollection, function (ctrlElement) {
// need to force the validation as the element might not be a known form input type
// so the normal validation process will ignore it.
processElement(ctrlElement, true);
});
}
return frmValid;
},
setElementValidationError = function (element, errorMsgKey, errorMsg) {
if (errorMsgKey) {
validator.getErrorMessage(errorMsgKey, element).then(function (msg) {
validator.makeInvalid(element, msg);
});
} else {
validator.makeInvalid(element, errorMsg);
}
};
return {
setElementValidationError: setElementValidationError,
validateElement: validateElement,
validateForm: validateForm,
resetElement: resetElement,
resetForm: resetForm
};
}
]);
}(angular));
(function (angular) {
'use strict';
angular.module('jcs-autoValidate').directive('form', [
'validationManager',
function (validationManager) {
return {
restrict: 'E',
link: function (scope, el) {
el.on('reset', function () {
validationManager.resetForm(el);
});
scope.$on('$destroy', function () {
el.off('reset');
});
}
};
}
]);
}(angular));
(function (angular) {
'use strict';
angular.module('jcs-autoValidate').directive('registerCustomFormControl', [
function () {
var findParentForm = function (el) {
var parent = el;
for (var i = 0; i <= 10; i += 1) {
if (parent !== undefined && parent.nodeName.toLowerCase() === 'form') {
break;
} else if (parent !== undefined) {
parent = angular.element(parent).parent()[0];
}
}
return parent;
};
return {
restrict: 'A',
link: function (scope, element) {
var frmEl = findParentForm(element.parent()[0]);
if (frmEl) {
frmEl.customHTMLFormControlsCollection = frmEl.customHTMLFormControlsCollection || [];
frmEl.customHTMLFormControlsCollection.push(element[0]);
}
}
};
}
]);
}(angular));
(function (angular) {
'use strict';
angular.module('jcs-autoValidate').directive('form', [
'validator',
function (validator) {
return {
restrict: 'E',
require: 'form',
compile: function () {
return {
pre: function (scope, element, attrs, ctrl) {
ctrl.disableDynamicValidation = !validator.isEnabled();
if (attrs.disableDynamicValidation !== undefined) {
ctrl.disableDynamicValidation = attrs.disableDynamicValidation === undefined || attrs.disableDynamicValidation === '' || attrs.disableDynamicValidation === 'true';
}
}
};
}
};
}
]);
}(angular));
(function (angular) {
'use strict';
angular.module('jcs-autoValidate').config(['$provide',
function ($provide) {
$provide.decorator('ngSubmitDirective', [
'$delegate',
'$parse',
'validationManager',
function ($delegate, $parse, validationManager) {
$delegate[0].compile = function ($element, attr) {
var fn = $parse(attr.ngSubmit),
force = attr.ngSubmitForce === 'true';
return function (scope, element) {
element.on('submit', function (event) {
scope.$apply(function () {
if (force === true || validationManager.validateForm(element)) {
fn(scope, {
$event: event
});
}
});
});
};
};
return $delegate;
}
]);
}
]);
}(angular));
(function (angular) {
'use strict';
angular.module('jcs-autoValidate').config(['$provide',
function ($provide) {
$provide.decorator('ngModelDirective', [
'$timeout',
'$delegate',
'validationManager',
'jcs-debounce',
function ($timeout, $delegate, validationManager, debounce) {
var directive = $delegate[0],
link = directive.link || directive.compile;
directive.compile = function (el) {
return function (scope, element, attrs, ctrls) {
var ngModelCtrl = ctrls[0],
frmCtrl = ctrls[1],
supportsNgModelOptions = angular.version.major >= 1 && angular.version.minor >= 3,
ngModelOptions = attrs.ngModelOptions === undefined ? undefined : scope.$eval(attrs.ngModelOptions),
setValidity = ngModelCtrl.$setValidity,
setPristine = ngModelCtrl.$setPristine,
setValidationState = debounce.debounce(function () {
validationManager.validateElement(ngModelCtrl, element);
}, 100);
// in the RC of 1.3 there is no directive.link only the directive.compile which
// needs to be invoked to get at the link functions.
if (supportsNgModelOptions && angular.isFunction(link)) {
link = link(el);
}
if (link.pre) {
link.pre.apply(this, arguments);
ngModelOptions = ngModelCtrl.$options === undefined ? undefined : ngModelCtrl.$options;
}
if (attrs.formnovalidate === undefined || (frmCtrl !== undefined && frmCtrl.disableDynamicValidation === false)) {
if (supportsNgModelOptions || ngModelOptions === undefined || ngModelOptions.updateOn === undefined || ngModelOptions.updateOn === '') {
ngModelCtrl.$setValidity = function (validationErrorKey, isValid) {
setValidity.call(ngModelCtrl, validationErrorKey, isValid);
setValidationState();
};
} else {
element.on(ngModelOptions.updateOn, function () {
setValidationState();
});
scope.$on('$destroy', function () {
element.off(ngModelOptions.updateOn);
});
}
// We override this so we can reset the element state when it is called.
ngModelCtrl.$setPristine = function () {
setPristine.call(ngModelCtrl);
validationManager.resetElement(element);
};
ngModelCtrl.autoValidated = true;
}
if (link.post) {
link.post.apply(this, arguments);
} else {
link.apply(this, arguments);
}
ngModelCtrl.setExternalValidation = function (errorMsgKey, errorMessage, addToModelErrors) {
if (addToModelErrors) {
if (ngModelCtrl.$error) {
ngModelCtrl.$error[errorMsgKey] = false;
} else {
ngModelCtrl.$errors[errorMsgKey] = false;
}
}
validationManager.setElementValidationError(element, errorMsgKey, errorMessage);
};
ngModelCtrl.removeExternalValidation = function (errorMsgKey, addToModelErrors) {
if (addToModelErrors) {
if (ngModelCtrl.$error) {
ngModelCtrl.$error[errorMsgKey] = true;
} else {
ngModelCtrl.$errors[errorMsgKey] = true;
}
}
validationManager.resetElement(element);
};
if (frmCtrl) {
frmCtrl.setExternalValidation = function (modelProperty, errorMsgKey, errorMessageOverride, addToModelErrors) {
var success = false;
if (frmCtrl[modelProperty]) {
frmCtrl[modelProperty].setExternalValidation(errorMsgKey, errorMessageOverride, addToModelErrors);
success = true;
}
return success;
};
frmCtrl.removeExternalValidation = function (modelProperty, errorMsgKey, errorMessageOverride, addToModelErrors) {
var success = false;
if (frmCtrl[modelProperty]) {
frmCtrl[modelProperty].removeExternalValidation(errorMsgKey, addToModelErrors);
success = true;
}
return success;
};
}
};
};
return $delegate;
}
]);
}
]);
}(angular));
(function (angular) {
'use strict';
angular.module('jcs-autoValidate')
.run([
'validator',
'defaultErrorMessageResolver',
'bootstrap3ElementModifier',
'foundation5ElementModifier',
function (validator, defaultErrorMessageResolver, bootstrap3ElementModifier, foundation5ElementModifier) {
validator.setErrorMessageResolver(defaultErrorMessageResolver.resolve);
validator.registerDomModifier(bootstrap3ElementModifier.key, bootstrap3ElementModifier);
validator.registerDomModifier(foundation5ElementModifier.key, foundation5ElementModifier);
validator.setDefaultElementModifier(bootstrap3ElementModifier.key);
}
]);
}(angular));