<!DOCTYPE html>
<html ng-app="test">
<head>
<script data-require="angular.js@1.3.14" data-semver="1.3.14" src="https://code.angularjs.org/1.3.14/angular.js"></script>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css" />
<script src="position.js"></script>
<script src="dateparser.js"></script>
<script src="datepicker_nofix.js"></script>
<script src="script.js"></script>
<script src="timezone.js"></script>
</head>
<body id="approot" ng-controller="MainCtrl as header">
<div ng-controller="TimeZoneCtrl as tz">
<h1>Hello From {{ tz.selectedTimeZone }}!</h1>
<input type="text" id="shipDate" class="form-control" datepicker-popup="shortDate"
datepicker-options="header.dp.options" required="true" ng-model-options="{ updateOn: 'default blur' }"
ng-click="header.dp.opened.dt['shipDate'] = !header.dp.opened.dt['shipDate']"
is-open="header.dp.opened.dt['shipDate']" min-date="header.dp.minDate"
ng-model="header.shipDate" show-button-bar="false" />
<br />
<label for="regionPicker">Pick a time zone region:</label>
<br />
<select id="regionPicker" class="form-control" ng-if="tz.tzData.all"
ng-options="value as key for (key, value) in tz.tzData.all" ng-model="tz.tzData.forCountry"></select>
<br />
<label for="cityPicker" ng-if="tz.tzData.forCountry">Pick a time zone sub-region:</label>
<br />
<select id="cityPicker" class="form-control" ng-if="tz.tzData.forCountry"
ng-options="value as key for (key, value) in tz.tzData.forCountry" ng-model="tz.tzData.selected"></select>
<br />
<pre>{{ header.tzData.selected | json }}</pre>
</div>
</body>
</html>
// Code goes here
const moduleName = 'test';
const ctrlName = 'MainCtrl';
var app = null;
var def = {};
def.module = {
name: moduleName,
dependencies: ['ui.bootstrap.datepicker']
};
def.controller = {
name: ctrlName,
dependencies: [],
definition: function() {
var self = this;
self.dp = {
options: {
datepickerMode: 'day',
showWeeks: false,
startingDay: 1,
showButtonBar: false
},
opened: {},
minDate: new Date(new Date().toISOString().substr(0, 10))
};
}
};
//bootstrap it up
app = angular.module(def.module.name, def.module.dependencies);
app.controller(def.controller.name, def.controller.dependencies.concat(def.controller.definition));
//EOF
angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootstrap.position'])
.constant('datepickerConfig', {
formatDay: 'dd',
formatMonth: 'MMMM',
formatYear: 'yyyy',
formatDayHeader: 'EEE',
formatDayTitle: 'MMMM yyyy',
formatMonthTitle: 'yyyy',
datepickerMode: 'day',
minMode: 'day',
maxMode: 'year',
showWeeks: true,
startingDay: 0,
yearRange: 20,
minDate: null,
maxDate: null
})
.controller('DatepickerController', ['$scope', '$attrs', '$parse', '$interpolate', '$timeout', '$log', 'dateFilter', 'datepickerConfig', function($scope, $attrs, $parse, $interpolate, $timeout, $log, dateFilter, datepickerConfig) {
var self = this,
ngModelCtrl = { $setViewValue: angular.noop }; // nullModelCtrl;
// Modes chain
this.modes = ['day', 'month', 'year'];
// Configuration attributes
angular.forEach(['formatDay', 'formatMonth', 'formatYear', 'formatDayHeader', 'formatDayTitle', 'formatMonthTitle',
'minMode', 'maxMode', 'showWeeks', 'startingDay', 'yearRange'], function( key, index ) {
self[key] = angular.isDefined($attrs[key]) ? (index < 8 ? $interpolate($attrs[key])($scope.$parent) : $scope.$parent.$eval($attrs[key])) : datepickerConfig[key];
});
// Watchable date attributes
angular.forEach(['minDate', 'maxDate'], function( key ) {
if ( $attrs[key] ) {
$scope.$parent.$watch($parse($attrs[key]), function(value) {
self[key] = value ? new Date(value) : null;
self.refreshView();
});
} else {
self[key] = datepickerConfig[key] ? new Date(datepickerConfig[key]) : null;
}
});
$scope.datepickerMode = $scope.datepickerMode || datepickerConfig.datepickerMode;
$scope.uniqueId = 'datepicker-' + $scope.$id + '-' + Math.floor(Math.random() * 10000);
this.activeDate = angular.isDefined($attrs.initDate) ? $scope.$parent.$eval($attrs.initDate) : new Date();
$scope.isActive = function(dateObject) {
if (self.compare(dateObject.date, self.activeDate) === 0) {
$scope.activeDateId = dateObject.uid;
return true;
}
return false;
};
this.init = function( ngModelCtrl_ ) {
ngModelCtrl = ngModelCtrl_;
ngModelCtrl.$render = function() {
self.render();
};
};
this.render = function() {
if ( ngModelCtrl.$modelValue ) {
var date = new Date( ngModelCtrl.$modelValue ),
isValid = !isNaN(date);
if ( isValid ) {
this.activeDate = date;
} else {
$log.error('Datepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.');
}
ngModelCtrl.$setValidity('date', isValid);
}
this.refreshView();
};
this.refreshView = function() {
if ( this.element ) {
this._refreshView();
var date = ngModelCtrl.$modelValue ? new Date(ngModelCtrl.$modelValue) : null;
ngModelCtrl.$setValidity('date-disabled', !date || (this.element && !this.isDisabled(date)));
}
};
this.createDateObject = function(date, format) {
var model = ngModelCtrl.$modelValue ? new Date(ngModelCtrl.$modelValue) : null;
return {
date: date,
label: dateFilter(date, format),
selected: model && this.compare(date, model) === 0,
disabled: this.isDisabled(date),
current: this.compare(date, new Date()) === 0
};
};
this.isDisabled = function( date ) {
return ((this.minDate && this.compare(date, this.minDate) < 0) || (this.maxDate && this.compare(date, this.maxDate) > 0) || ($attrs.dateDisabled && $scope.dateDisabled({date: date, mode: $scope.datepickerMode})));
};
// Split array into smaller arrays
this.split = function(arr, size) {
var arrays = [];
while (arr.length > 0) {
arrays.push(arr.splice(0, size));
}
return arrays;
};
$scope.select = function( date ) {
if ( $scope.datepickerMode === self.minMode ) {
var dt = ngModelCtrl.$modelValue ? new Date( ngModelCtrl.$modelValue ) : new Date(0, 0, 0, 0, 0, 0, 0);
dt.setFullYear( date.getFullYear(), date.getMonth(), date.getDate() );
ngModelCtrl.$setViewValue( dt );
ngModelCtrl.$render();
} else {
self.activeDate = date;
$scope.datepickerMode = self.modes[ self.modes.indexOf( $scope.datepickerMode ) - 1 ];
}
//$timeout(function () { self.activeDate = date; });
};
$scope.move = function( direction ) {
var year = self.activeDate.getFullYear() + direction * (self.step.years || 0),
month = self.activeDate.getMonth() + direction * (self.step.months || 0);
self.activeDate.setFullYear(year, month, 1);
self.refreshView();
};
$scope.toggleMode = function( direction ) {
direction = direction || 1;
if (($scope.datepickerMode === self.maxMode && direction === 1) || ($scope.datepickerMode === self.minMode && direction === -1)) {
return;
}
$scope.datepickerMode = self.modes[ self.modes.indexOf( $scope.datepickerMode ) + direction ];
};
// Key event mapper
$scope.keys = { 13:'enter', 32:'space', 33:'pageup', 34:'pagedown', 35:'end', 36:'home', 37:'left', 38:'up', 39:'right', 40:'down' };
var focusElement = function() {
$timeout(function() {
self.element[0].focus();
}, 0 , false);
};
// Listen for focus requests from popup directive
$scope.$on('datepicker.focus', focusElement);
$scope.keydown = function( evt ) {
var key = $scope.keys[evt.which];
if ( !key || evt.shiftKey || evt.altKey ) {
return;
}
evt.preventDefault();
evt.stopPropagation();
if (key === 'enter' || key === 'space') {
if ( self.isDisabled(self.activeDate)) {
return; // do nothing
}
$scope.select(self.activeDate);
focusElement();
} else if (evt.ctrlKey && (key === 'up' || key === 'down')) {
$scope.toggleMode(key === 'up' ? 1 : -1);
focusElement();
} else {
self.handleKeyDown(key, evt);
self.refreshView();
}
};
}])
.directive( 'datepicker', function () {
return {
restrict: 'EA',
replace: true,
templateUrl: 'template/datepicker/datepicker.html',
scope: {
datepickerMode: '=?',
dateDisabled: '&'
},
require: ['datepicker', '?^ngModel'],
controller: 'DatepickerController',
link: function(scope, element, attrs, ctrls) {
var datepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1];
if ( ngModelCtrl ) {
datepickerCtrl.init( ngModelCtrl );
}
}
};
})
.directive('daypicker', ['dateFilter', function (dateFilter) {
return {
restrict: 'EA',
replace: true,
templateUrl: 'template/datepicker/day.html',
require: '^datepicker',
link: function(scope, element, attrs, ctrl) {
scope.showWeeks = ctrl.showWeeks;
ctrl.step = { months: 1 };
ctrl.element = element;
var DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
function getDaysInMonth( year, month ) {
return ((month === 1) && (year % 4 === 0) && ((year % 100 !== 0) || (year % 400 === 0))) ? 29 : DAYS_IN_MONTH[month];
}
function getDates(startDate, n) {
var dates = new Array(n), current = new Date(startDate), i = 0;
current.setHours(12); // Prevent repeated dates because of timezone bug
while ( i < n ) {
dates[i++] = new Date(current);
current.setDate( current.getDate() + 1 );
}
return dates;
}
ctrl._refreshView = function() {
var year = ctrl.activeDate.getFullYear(),
month = ctrl.activeDate.getMonth(),
firstDayOfMonth = new Date(year, month, 1),
difference = ctrl.startingDay - firstDayOfMonth.getDay(),
numDisplayedFromPreviousMonth = (difference > 0) ? 7 - difference : - difference,
firstDate = new Date(firstDayOfMonth);
if ( numDisplayedFromPreviousMonth > 0 ) {
firstDate.setDate( - numDisplayedFromPreviousMonth + 1 );
}
// 42 is the number of days on a six-month calendar
var days = getDates(firstDate, 42);
for (var i = 0; i < 42; i ++) {
days[i] = angular.extend(ctrl.createDateObject(days[i], ctrl.formatDay), {
secondary: days[i].getMonth() !== month,
uid: scope.uniqueId + '-' + i
});
}
scope.labels = new Array(7);
for (var j = 0; j < 7; j++) {
scope.labels[j] = {
abbr: dateFilter(days[j].date, ctrl.formatDayHeader),
full: dateFilter(days[j].date, 'EEEE')
};
}
scope.title = dateFilter(ctrl.activeDate, ctrl.formatDayTitle);
scope.rows = ctrl.split(days, 7);
if ( scope.showWeeks ) {
scope.weekNumbers = [];
var weekNumber = getISO8601WeekNumber( scope.rows[0][0].date ),
numWeeks = scope.rows.length;
while( scope.weekNumbers.push(weekNumber++) < numWeeks ) {}
}
};
ctrl.compare = function(date1, date2) {
return (new Date( date1.getFullYear(), date1.getMonth(), date1.getDate() ) - new Date( date2.getFullYear(), date2.getMonth(), date2.getDate() ) );
};
function getISO8601WeekNumber(date) {
var checkDate = new Date(date);
checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7)); // Thursday
var time = checkDate.getTime();
checkDate.setMonth(0); // Compare with Jan 1
checkDate.setDate(1);
return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
}
ctrl.handleKeyDown = function( key, evt ) {
var date = ctrl.activeDate.getDate();
if (key === 'left') {
date = date - 1; // up
} else if (key === 'up') {
date = date - 7; // down
} else if (key === 'right') {
date = date + 1; // down
} else if (key === 'down') {
date = date + 7;
} else if (key === 'pageup' || key === 'pagedown') {
var month = ctrl.activeDate.getMonth() + (key === 'pageup' ? - 1 : 1);
ctrl.activeDate.setMonth(month, 1);
date = Math.min(getDaysInMonth(ctrl.activeDate.getFullYear(), ctrl.activeDate.getMonth()), date);
} else if (key === 'home') {
date = 1;
} else if (key === 'end') {
date = getDaysInMonth(ctrl.activeDate.getFullYear(), ctrl.activeDate.getMonth());
}
ctrl.activeDate.setDate(date);
};
ctrl.refreshView();
}
};
}])
.directive('monthpicker', ['dateFilter', function (dateFilter) {
return {
restrict: 'EA',
replace: true,
templateUrl: 'template/datepicker/month.html',
require: '^datepicker',
link: function(scope, element, attrs, ctrl) {
ctrl.step = { years: 1 };
ctrl.element = element;
ctrl._refreshView = function() {
var months = new Array(12),
year = ctrl.activeDate.getFullYear();
for ( var i = 0; i < 12; i++ ) {
months[i] = angular.extend(ctrl.createDateObject(new Date(year, i, 1), ctrl.formatMonth), {
uid: scope.uniqueId + '-' + i
});
}
scope.title = dateFilter(ctrl.activeDate, ctrl.formatMonthTitle);
scope.rows = ctrl.split(months, 3);
};
ctrl.compare = function(date1, date2) {
return new Date( date1.getFullYear(), date1.getMonth() ) - new Date( date2.getFullYear(), date2.getMonth() );
};
ctrl.handleKeyDown = function( key, evt ) {
var date = ctrl.activeDate.getMonth();
if (key === 'left') {
date = date - 1; // up
} else if (key === 'up') {
date = date - 3; // down
} else if (key === 'right') {
date = date + 1; // down
} else if (key === 'down') {
date = date + 3;
} else if (key === 'pageup' || key === 'pagedown') {
var year = ctrl.activeDate.getFullYear() + (key === 'pageup' ? - 1 : 1);
ctrl.activeDate.setFullYear(year);
} else if (key === 'home') {
date = 0;
} else if (key === 'end') {
date = 11;
}
ctrl.activeDate.setMonth(date);
};
ctrl.refreshView();
}
};
}])
.directive('yearpicker', ['dateFilter', function (dateFilter) {
return {
restrict: 'EA',
replace: true,
templateUrl: 'template/datepicker/year.html',
require: '^datepicker',
link: function(scope, element, attrs, ctrl) {
var range = ctrl.yearRange;
ctrl.step = { years: range };
ctrl.element = element;
function getStartingYear( year ) {
return parseInt((year - 1) / range, 10) * range + 1;
}
ctrl._refreshView = function() {
var years = new Array(range);
for ( var i = 0, start = getStartingYear(ctrl.activeDate.getFullYear()); i < range; i++ ) {
years[i] = angular.extend(ctrl.createDateObject(new Date(start + i, 0, 1), ctrl.formatYear), {
uid: scope.uniqueId + '-' + i
});
}
scope.title = [years[0].label, years[range - 1].label].join(' - ');
scope.rows = ctrl.split(years, 5);
};
ctrl.compare = function(date1, date2) {
return date1.getFullYear() - date2.getFullYear();
};
ctrl.handleKeyDown = function( key, evt ) {
var date = ctrl.activeDate.getFullYear();
if (key === 'left') {
date = date - 1; // up
} else if (key === 'up') {
date = date - 5; // down
} else if (key === 'right') {
date = date + 1; // down
} else if (key === 'down') {
date = date + 5;
} else if (key === 'pageup' || key === 'pagedown') {
date += (key === 'pageup' ? - 1 : 1) * ctrl.step.years;
} else if (key === 'home') {
date = getStartingYear( ctrl.activeDate.getFullYear() );
} else if (key === 'end') {
date = getStartingYear( ctrl.activeDate.getFullYear() ) + range - 1;
}
ctrl.activeDate.setFullYear(date);
};
ctrl.refreshView();
}
};
}])
.constant('datepickerPopupConfig', {
datepickerPopup: 'yyyy-MM-dd',
currentText: 'Today',
clearText: 'Clear',
closeText: 'Done',
closeOnDateSelection: true,
appendToBody: false,
showButtonBar: true
})
.directive('datepickerPopup', ['$compile', '$parse', '$document', '$position', 'dateFilter', 'dateParser', 'datepickerPopupConfig',
function ($compile, $parse, $document, $position, dateFilter, dateParser, datepickerPopupConfig) {
return {
restrict: 'EA',
require: 'ngModel',
scope: {
isOpen: '=?',
currentText: '@',
clearText: '@',
closeText: '@',
dateDisabled: '&'
},
link: function(scope, element, attrs, ngModel) {
var dateFormat,
closeOnDateSelection = angular.isDefined(attrs.closeOnDateSelection) ? scope.$parent.$eval(attrs.closeOnDateSelection) : datepickerPopupConfig.closeOnDateSelection,
appendToBody = angular.isDefined(attrs.datepickerAppendToBody) ? scope.$parent.$eval(attrs.datepickerAppendToBody) : datepickerPopupConfig.appendToBody;
scope.showButtonBar = angular.isDefined(attrs.showButtonBar) ? scope.$parent.$eval(attrs.showButtonBar) : datepickerPopupConfig.showButtonBar;
scope.getText = function( key ) {
return scope[key + 'Text'] || datepickerPopupConfig[key + 'Text'];
};
attrs.$observe('datepickerPopup', function(value) {
dateFormat = value || datepickerPopupConfig.datepickerPopup;
ngModel.$render();
});
// popup element used to display calendar
var popupEl = angular.element('<div datepicker-popup-wrap><div datepicker></div></div>');
popupEl.attr({
'ng-model': 'date',
'ng-change': 'dateSelection()'
});
function cameltoDash( string ){
return string.replace(/([A-Z])/g, function($1) { return '-' + $1.toLowerCase(); });
}
// datepicker element
var datepickerEl = angular.element(popupEl.children()[0]);
if ( attrs.datepickerOptions ) {
angular.forEach(scope.$parent.$eval(attrs.datepickerOptions), function( value, option ) {
datepickerEl.attr( cameltoDash(option), value );
});
}
scope.watchData = {};
angular.forEach(['minDate', 'maxDate', 'datepickerMode'], function( key ) {
if ( attrs[key] ) {
var getAttribute = $parse(attrs[key]);
scope.$parent.$watch(getAttribute, function(value){
scope.watchData[key] = value;
});
datepickerEl.attr(cameltoDash(key), 'watchData.' + key);
// Propagate changes from datepicker to outside
if ( key === 'datepickerMode' ) {
var setAttribute = getAttribute.assign;
scope.$watch('watchData.' + key, function(value, oldvalue) {
if ( value !== oldvalue ) {
setAttribute(scope.$parent, value);
}
});
}
}
});
if (attrs.dateDisabled) {
datepickerEl.attr('date-disabled', 'dateDisabled({ date: date, mode: mode })');
}
function parseDate(viewValue) {
if (!viewValue) {
ngModel.$setValidity('date', true);
return null;
} else if (angular.isDate(viewValue) && !isNaN(viewValue)) {
ngModel.$setValidity('date', true);
return viewValue;
} else if (angular.isString(viewValue)) {
var date = dateParser.parse(viewValue, dateFormat) || new Date(viewValue);
if (isNaN(date)) {
ngModel.$setValidity('date', false);
return undefined;
} else {
ngModel.$setValidity('date', true);
return date;
}
} else {
ngModel.$setValidity('date', false);
return undefined;
}
}
ngModel.$parsers.unshift(parseDate);
ngModel.$formatters.push(function (value) {
return ngModel.$isEmpty(value) ? value : dateFilter(value, dateFormat);
});
// Inner change
scope.dateSelection = function(dt) {
if (angular.isDefined(dt)) {
scope.date = dt;
}
ngModel.$setViewValue(scope.date);
ngModel.$render();
if ( closeOnDateSelection ) {
scope.isOpen = false;
element[0].focus();
}
};
element.bind('input change keyup', function() {
scope.$apply(function() {
scope.date = ngModel.$modelValue;
});
});
// Outer change
ngModel.$render = function() {
var date = ngModel.$viewValue ? dateFilter(parseDate(ngModel.$viewValue), dateFormat) : '';
element.val(date);
scope.date = parseDate( ngModel.$modelValue );
};
var documentClickBind = function(event) {
if (scope.isOpen && event.target !== element[0]) {
scope.$apply(function() {
scope.isOpen = false;
});
}
};
var keydown = function(evt, noApply) {
scope.keydown(evt);
};
element.bind('keydown', keydown);
scope.keydown = function(evt) {
if (evt.which === 27) {
evt.preventDefault();
evt.stopPropagation();
scope.close();
} else if (evt.which === 40 && !scope.isOpen) {
scope.isOpen = true;
}
};
scope.$watch('isOpen', function(value) {
if (value) {
scope.$broadcast('datepicker.focus');
scope.position = appendToBody ? $position.offset(element) : $position.position(element);
scope.position.top = scope.position.top + element.prop('offsetHeight');
$document.bind('click', documentClickBind);
} else {
$document.unbind('click', documentClickBind);
}
});
scope.select = function( date ) {
if (date === 'today') {
var today = new Date();
if (angular.isDate(ngModel.$modelValue)) {
date = new Date(ngModel.$modelValue);
date.setFullYear(today.getFullYear(), today.getMonth(), today.getDate());
} else {
date = new Date(today.setHours(0, 0, 0, 0));
}
}
scope.dateSelection( date );
};
scope.close = function() {
scope.isOpen = false;
element[0].focus();
};
var $popup = $compile(popupEl)(scope);
// Prevent jQuery cache memory leak (template is now redundant after linking)
popupEl.remove();
if ( appendToBody ) {
$document.find('body').append($popup);
} else {
element.after($popup);
}
scope.$on('$destroy', function() {
$popup.remove();
element.unbind('keydown', keydown);
$document.unbind('click', documentClickBind);
});
}
};
}])
.directive('datepickerPopupWrap', function() {
return {
restrict:'EA',
replace: true,
transclude: true,
templateUrl: 'template/datepicker/popup.html',
link:function (scope, element, attrs) {
element.bind('click', function(event) {
event.preventDefault();
event.stopPropagation();
});
}
};
});
angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootstrap.position'])
.constant('datepickerConfig', {
formatDay: 'dd',
formatMonth: 'MMMM',
formatYear: 'yyyy',
formatDayHeader: 'EEE',
formatDayTitle: 'MMMM yyyy',
formatMonthTitle: 'yyyy',
datepickerMode: 'day',
minMode: 'day',
maxMode: 'year',
showWeeks: true,
startingDay: 0,
yearRange: 20,
minDate: null,
maxDate: null
})
.controller('DatepickerController', ['$scope', '$attrs', '$parse', '$interpolate', '$timeout', '$log', 'dateFilter', 'datepickerConfig', function($scope, $attrs, $parse, $interpolate, $timeout, $log, dateFilter, datepickerConfig) {
var self = this,
ngModelCtrl = { $setViewValue: angular.noop }; // nullModelCtrl;
// Modes chain
this.modes = ['day', 'month', 'year'];
// Configuration attributes
angular.forEach(['formatDay', 'formatMonth', 'formatYear', 'formatDayHeader', 'formatDayTitle', 'formatMonthTitle',
'minMode', 'maxMode', 'showWeeks', 'startingDay', 'yearRange'], function( key, index ) {
self[key] = angular.isDefined($attrs[key]) ? (index < 8 ? $interpolate($attrs[key])($scope.$parent) : $scope.$parent.$eval($attrs[key])) : datepickerConfig[key];
});
// Watchable date attributes
angular.forEach(['minDate', 'maxDate'], function( key ) {
if ( $attrs[key] ) {
$scope.$parent.$watch($parse($attrs[key]), function(value) {
self[key] = value ? new Date(value) : null;
self.refreshView();
});
} else {
self[key] = datepickerConfig[key] ? new Date(datepickerConfig[key]) : null;
}
});
$scope.datepickerMode = $scope.datepickerMode || datepickerConfig.datepickerMode;
$scope.uniqueId = 'datepicker-' + $scope.$id + '-' + Math.floor(Math.random() * 10000);
this.activeDate = angular.isDefined($attrs.initDate) ? $scope.$parent.$eval($attrs.initDate) : new Date();
$scope.isActive = function(dateObject) {
if (self.compare(dateObject.date, self.activeDate) === 0) {
$scope.activeDateId = dateObject.uid;
return true;
}
return false;
};
this.init = function( ngModelCtrl_ ) {
ngModelCtrl = ngModelCtrl_;
ngModelCtrl.$render = function() {
self.render();
};
};
this.render = function() {
if ( ngModelCtrl.$modelValue ) {
var date = new Date( ngModelCtrl.$modelValue ),
isValid = !isNaN(date);
if ( isValid ) {
this.activeDate = date;
} else {
$log.error('Datepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.');
}
ngModelCtrl.$setValidity('date', isValid);
}
this.refreshView();
};
this.refreshView = function() {
if ( this.element ) {
this._refreshView();
var date = ngModelCtrl.$modelValue ? new Date(ngModelCtrl.$modelValue) : null;
ngModelCtrl.$setValidity('date-disabled', !date || (this.element && !this.isDisabled(date)));
}
};
this.createDateObject = function(date, format) {
var model = ngModelCtrl.$modelValue ? new Date(ngModelCtrl.$modelValue) : null;
return {
date: date,
label: dateFilter(date, format),
selected: model && this.compare(date, model) === 0,
disabled: this.isDisabled(date),
current: this.compare(date, new Date()) === 0
};
};
this.isDisabled = function( date ) {
return ((this.minDate && this.compare(date, this.minDate) < 0) || (this.maxDate && this.compare(date, this.maxDate) > 0) || ($attrs.dateDisabled && $scope.dateDisabled({date: date, mode: $scope.datepickerMode})));
};
// Split array into smaller arrays
this.split = function(arr, size) {
var arrays = [];
while (arr.length > 0) {
arrays.push(arr.splice(0, size));
}
return arrays;
};
$scope.select = function( date ) {
if ( $scope.datepickerMode === self.minMode ) {
var dt = ngModelCtrl.$modelValue ? new Date( ngModelCtrl.$modelValue ) : new Date(0, 0, 0, 0, 0, 0, 0);
dt.setFullYear( date.getFullYear(), date.getMonth(), date.getDate() );
ngModelCtrl.$setViewValue( dt );
ngModelCtrl.$render();
} else {
self.activeDate = date;
$scope.datepickerMode = self.modes[ self.modes.indexOf( $scope.datepickerMode ) - 1 ];
}
$timeout(function () { self.activeDate = date; });
};
$scope.move = function( direction ) {
var year = self.activeDate.getFullYear() + direction * (self.step.years || 0),
month = self.activeDate.getMonth() + direction * (self.step.months || 0);
self.activeDate.setFullYear(year, month, 1);
self.refreshView();
};
$scope.toggleMode = function( direction ) {
direction = direction || 1;
if (($scope.datepickerMode === self.maxMode && direction === 1) || ($scope.datepickerMode === self.minMode && direction === -1)) {
return;
}
$scope.datepickerMode = self.modes[ self.modes.indexOf( $scope.datepickerMode ) + direction ];
};
// Key event mapper
$scope.keys = { 13:'enter', 32:'space', 33:'pageup', 34:'pagedown', 35:'end', 36:'home', 37:'left', 38:'up', 39:'right', 40:'down' };
var focusElement = function() {
$timeout(function() {
self.element[0].focus();
}, 0 , false);
};
// Listen for focus requests from popup directive
$scope.$on('datepicker.focus', focusElement);
$scope.keydown = function( evt ) {
var key = $scope.keys[evt.which];
if ( !key || evt.shiftKey || evt.altKey ) {
return;
}
evt.preventDefault();
evt.stopPropagation();
if (key === 'enter' || key === 'space') {
if ( self.isDisabled(self.activeDate)) {
return; // do nothing
}
$scope.select(self.activeDate);
focusElement();
} else if (evt.ctrlKey && (key === 'up' || key === 'down')) {
$scope.toggleMode(key === 'up' ? 1 : -1);
focusElement();
} else {
self.handleKeyDown(key, evt);
self.refreshView();
}
};
}])
.directive( 'datepicker', function () {
return {
restrict: 'EA',
replace: true,
templateUrl: 'template/datepicker/datepicker.html',
scope: {
datepickerMode: '=?',
dateDisabled: '&'
},
require: ['datepicker', '?^ngModel'],
controller: 'DatepickerController',
link: function(scope, element, attrs, ctrls) {
var datepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1];
if ( ngModelCtrl ) {
datepickerCtrl.init( ngModelCtrl );
}
}
};
})
.directive('daypicker', ['dateFilter', function (dateFilter) {
return {
restrict: 'EA',
replace: true,
templateUrl: 'template/datepicker/day.html',
require: '^datepicker',
link: function(scope, element, attrs, ctrl) {
scope.showWeeks = ctrl.showWeeks;
ctrl.step = { months: 1 };
ctrl.element = element;
var DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
function getDaysInMonth( year, month ) {
return ((month === 1) && (year % 4 === 0) && ((year % 100 !== 0) || (year % 400 === 0))) ? 29 : DAYS_IN_MONTH[month];
}
function getDates(startDate, n) {
var dates = new Array(n), current = new Date(startDate), i = 0;
current.setHours(12); // Prevent repeated dates because of timezone bug
while ( i < n ) {
dates[i++] = new Date(current);
current.setDate( current.getDate() + 1 );
}
return dates;
}
ctrl._refreshView = function() {
var year = ctrl.activeDate.getFullYear(),
month = ctrl.activeDate.getMonth(),
firstDayOfMonth = new Date(year, month, 1),
difference = ctrl.startingDay - firstDayOfMonth.getDay(),
numDisplayedFromPreviousMonth = (difference > 0) ? 7 - difference : - difference,
firstDate = new Date(firstDayOfMonth);
if ( numDisplayedFromPreviousMonth > 0 ) {
firstDate.setDate( - numDisplayedFromPreviousMonth + 1 );
}
// 42 is the number of days on a six-month calendar
var days = getDates(firstDate, 42);
for (var i = 0; i < 42; i ++) {
days[i] = angular.extend(ctrl.createDateObject(days[i], ctrl.formatDay), {
secondary: days[i].getMonth() !== month,
uid: scope.uniqueId + '-' + i
});
}
scope.labels = new Array(7);
for (var j = 0; j < 7; j++) {
scope.labels[j] = {
abbr: dateFilter(days[j].date, ctrl.formatDayHeader),
full: dateFilter(days[j].date, 'EEEE')
};
}
scope.title = dateFilter(ctrl.activeDate, ctrl.formatDayTitle);
scope.rows = ctrl.split(days, 7);
if ( scope.showWeeks ) {
scope.weekNumbers = [];
var weekNumber = getISO8601WeekNumber( scope.rows[0][0].date ),
numWeeks = scope.rows.length;
while( scope.weekNumbers.push(weekNumber++) < numWeeks ) {}
}
};
ctrl.compare = function(date1, date2) {
return (new Date( date1.getFullYear(), date1.getMonth(), date1.getDate() ) - new Date( date2.getFullYear(), date2.getMonth(), date2.getDate() ) );
};
function getISO8601WeekNumber(date) {
var checkDate = new Date(date);
checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7)); // Thursday
var time = checkDate.getTime();
checkDate.setMonth(0); // Compare with Jan 1
checkDate.setDate(1);
return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
}
ctrl.handleKeyDown = function( key, evt ) {
var date = ctrl.activeDate.getDate();
if (key === 'left') {
date = date - 1; // up
} else if (key === 'up') {
date = date - 7; // down
} else if (key === 'right') {
date = date + 1; // down
} else if (key === 'down') {
date = date + 7;
} else if (key === 'pageup' || key === 'pagedown') {
var month = ctrl.activeDate.getMonth() + (key === 'pageup' ? - 1 : 1);
ctrl.activeDate.setMonth(month, 1);
date = Math.min(getDaysInMonth(ctrl.activeDate.getFullYear(), ctrl.activeDate.getMonth()), date);
} else if (key === 'home') {
date = 1;
} else if (key === 'end') {
date = getDaysInMonth(ctrl.activeDate.getFullYear(), ctrl.activeDate.getMonth());
}
ctrl.activeDate.setDate(date);
};
ctrl.refreshView();
}
};
}])
.directive('monthpicker', ['dateFilter', function (dateFilter) {
return {
restrict: 'EA',
replace: true,
templateUrl: 'template/datepicker/month.html',
require: '^datepicker',
link: function(scope, element, attrs, ctrl) {
ctrl.step = { years: 1 };
ctrl.element = element;
ctrl._refreshView = function() {
var months = new Array(12),
year = ctrl.activeDate.getFullYear();
for ( var i = 0; i < 12; i++ ) {
months[i] = angular.extend(ctrl.createDateObject(new Date(year, i, 1), ctrl.formatMonth), {
uid: scope.uniqueId + '-' + i
});
}
scope.title = dateFilter(ctrl.activeDate, ctrl.formatMonthTitle);
scope.rows = ctrl.split(months, 3);
};
ctrl.compare = function(date1, date2) {
return new Date( date1.getFullYear(), date1.getMonth() ) - new Date( date2.getFullYear(), date2.getMonth() );
};
ctrl.handleKeyDown = function( key, evt ) {
var date = ctrl.activeDate.getMonth();
if (key === 'left') {
date = date - 1; // up
} else if (key === 'up') {
date = date - 3; // down
} else if (key === 'right') {
date = date + 1; // down
} else if (key === 'down') {
date = date + 3;
} else if (key === 'pageup' || key === 'pagedown') {
var year = ctrl.activeDate.getFullYear() + (key === 'pageup' ? - 1 : 1);
ctrl.activeDate.setFullYear(year);
} else if (key === 'home') {
date = 0;
} else if (key === 'end') {
date = 11;
}
ctrl.activeDate.setMonth(date);
};
ctrl.refreshView();
}
};
}])
.directive('yearpicker', ['dateFilter', function (dateFilter) {
return {
restrict: 'EA',
replace: true,
templateUrl: 'template/datepicker/year.html',
require: '^datepicker',
link: function(scope, element, attrs, ctrl) {
var range = ctrl.yearRange;
ctrl.step = { years: range };
ctrl.element = element;
function getStartingYear( year ) {
return parseInt((year - 1) / range, 10) * range + 1;
}
ctrl._refreshView = function() {
var years = new Array(range);
for ( var i = 0, start = getStartingYear(ctrl.activeDate.getFullYear()); i < range; i++ ) {
years[i] = angular.extend(ctrl.createDateObject(new Date(start + i, 0, 1), ctrl.formatYear), {
uid: scope.uniqueId + '-' + i
});
}
scope.title = [years[0].label, years[range - 1].label].join(' - ');
scope.rows = ctrl.split(years, 5);
};
ctrl.compare = function(date1, date2) {
return date1.getFullYear() - date2.getFullYear();
};
ctrl.handleKeyDown = function( key, evt ) {
var date = ctrl.activeDate.getFullYear();
if (key === 'left') {
date = date - 1; // up
} else if (key === 'up') {
date = date - 5; // down
} else if (key === 'right') {
date = date + 1; // down
} else if (key === 'down') {
date = date + 5;
} else if (key === 'pageup' || key === 'pagedown') {
date += (key === 'pageup' ? - 1 : 1) * ctrl.step.years;
} else if (key === 'home') {
date = getStartingYear( ctrl.activeDate.getFullYear() );
} else if (key === 'end') {
date = getStartingYear( ctrl.activeDate.getFullYear() ) + range - 1;
}
ctrl.activeDate.setFullYear(date);
};
ctrl.refreshView();
}
};
}])
.constant('datepickerPopupConfig', {
datepickerPopup: 'yyyy-MM-dd',
currentText: 'Today',
clearText: 'Clear',
closeText: 'Done',
closeOnDateSelection: true,
appendToBody: false,
showButtonBar: true
})
.directive('datepickerPopup', ['$compile', '$parse', '$document', '$position', 'dateFilter', 'dateParser', 'datepickerPopupConfig',
function ($compile, $parse, $document, $position, dateFilter, dateParser, datepickerPopupConfig) {
return {
restrict: 'EA',
require: 'ngModel',
scope: {
isOpen: '=?',
currentText: '@',
clearText: '@',
closeText: '@',
dateDisabled: '&'
},
link: function(scope, element, attrs, ngModel) {
var dateFormat,
closeOnDateSelection = angular.isDefined(attrs.closeOnDateSelection) ? scope.$parent.$eval(attrs.closeOnDateSelection) : datepickerPopupConfig.closeOnDateSelection,
appendToBody = angular.isDefined(attrs.datepickerAppendToBody) ? scope.$parent.$eval(attrs.datepickerAppendToBody) : datepickerPopupConfig.appendToBody;
scope.showButtonBar = angular.isDefined(attrs.showButtonBar) ? scope.$parent.$eval(attrs.showButtonBar) : datepickerPopupConfig.showButtonBar;
scope.getText = function( key ) {
return scope[key + 'Text'] || datepickerPopupConfig[key + 'Text'];
};
attrs.$observe('datepickerPopup', function(value) {
dateFormat = value || datepickerPopupConfig.datepickerPopup;
ngModel.$render();
});
// popup element used to display calendar
var popupEl = angular.element('<div datepicker-popup-wrap><div datepicker></div></div>');
popupEl.attr({
'ng-model': 'date',
'ng-change': 'dateSelection()'
});
function cameltoDash( string ){
return string.replace(/([A-Z])/g, function($1) { return '-' + $1.toLowerCase(); });
}
// datepicker element
var datepickerEl = angular.element(popupEl.children()[0]);
if ( attrs.datepickerOptions ) {
angular.forEach(scope.$parent.$eval(attrs.datepickerOptions), function( value, option ) {
datepickerEl.attr( cameltoDash(option), value );
});
}
scope.watchData = {};
angular.forEach(['minDate', 'maxDate', 'datepickerMode'], function( key ) {
if ( attrs[key] ) {
var getAttribute = $parse(attrs[key]);
scope.$parent.$watch(getAttribute, function(value){
scope.watchData[key] = value;
});
datepickerEl.attr(cameltoDash(key), 'watchData.' + key);
// Propagate changes from datepicker to outside
if ( key === 'datepickerMode' ) {
var setAttribute = getAttribute.assign;
scope.$watch('watchData.' + key, function(value, oldvalue) {
if ( value !== oldvalue ) {
setAttribute(scope.$parent, value);
}
});
}
}
});
if (attrs.dateDisabled) {
datepickerEl.attr('date-disabled', 'dateDisabled({ date: date, mode: mode })');
}
function parseDate(viewValue) {
if (!viewValue) {
ngModel.$setValidity('date', true);
return null;
} else if (angular.isDate(viewValue) && !isNaN(viewValue)) {
ngModel.$setValidity('date', true);
return viewValue;
} else if (angular.isString(viewValue)) {
var date = dateParser.parse(viewValue, dateFormat) || new Date(viewValue);
if (isNaN(date)) {
ngModel.$setValidity('date', false);
return undefined;
} else {
ngModel.$setValidity('date', true);
return date;
}
} else {
ngModel.$setValidity('date', false);
return undefined;
}
}
ngModel.$parsers.unshift(parseDate);
ngModel.$formatters.push(function (value) {
return ngModel.$isEmpty(value) ? value : dateFilter(value, dateFormat);
});
// Inner change
scope.dateSelection = function(dt) {
if (angular.isDefined(dt)) {
scope.date = dt;
}
ngModel.$setViewValue(scope.date);
ngModel.$render();
if ( closeOnDateSelection ) {
scope.isOpen = false;
element[0].focus();
}
};
element.bind('input change keyup', function() {
scope.$apply(function() {
scope.date = ngModel.$modelValue;
});
});
// Outer change
ngModel.$render = function() {
var date = ngModel.$viewValue ? dateFilter(parseDate(ngModel.$viewValue), dateFormat) : '';
element.val(date);
scope.date = parseDate( ngModel.$modelValue );
};
var documentClickBind = function(event) {
if (scope.isOpen && event.target !== element[0]) {
scope.$apply(function() {
scope.isOpen = false;
});
}
};
var keydown = function(evt, noApply) {
scope.keydown(evt);
};
element.bind('keydown', keydown);
scope.keydown = function(evt) {
if (evt.which === 27) {
evt.preventDefault();
evt.stopPropagation();
scope.close();
} else if (evt.which === 40 && !scope.isOpen) {
scope.isOpen = true;
}
};
scope.$watch('isOpen', function(value) {
if (value) {
scope.$broadcast('datepicker.focus');
scope.position = appendToBody ? $position.offset(element) : $position.position(element);
scope.position.top = scope.position.top + element.prop('offsetHeight');
$document.bind('click', documentClickBind);
} else {
$document.unbind('click', documentClickBind);
}
});
scope.select = function( date ) {
if (date === 'today') {
var today = new Date();
if (angular.isDate(ngModel.$modelValue)) {
date = new Date(ngModel.$modelValue);
date.setFullYear(today.getFullYear(), today.getMonth(), today.getDate());
} else {
date = new Date(today.setHours(0, 0, 0, 0));
}
}
scope.dateSelection( date );
};
scope.close = function() {
scope.isOpen = false;
element[0].focus();
};
var $popup = $compile(popupEl)(scope);
// Prevent jQuery cache memory leak (template is now redundant after linking)
popupEl.remove();
if ( appendToBody ) {
$document.find('body').append($popup);
} else {
element.after($popup);
}
scope.$on('$destroy', function() {
$popup.remove();
element.unbind('keydown', keydown);
$document.unbind('click', documentClickBind);
});
}
};
}])
.directive('datepickerPopupWrap', function() {
return {
restrict:'EA',
replace: true,
transclude: true,
templateUrl: 'template/datepicker/popup.html',
link:function (scope, element, attrs) {
element.bind('click', function(event) {
event.preventDefault();
event.stopPropagation();
});
}
};
});
angular.module('ui.bootstrap.dateparser', [])
.service('dateParser', ['$locale', 'orderByFilter', function($locale, orderByFilter) {
this.parsers = {};
var formatCodeToRegex = {
'yyyy': {
regex: '\\d{4}',
apply: function(value) { this.year = +value; }
},
'yy': {
regex: '\\d{2}',
apply: function(value) { this.year = +value + 2000; }
},
'y': {
regex: '\\d{1,4}',
apply: function(value) { this.year = +value; }
},
'MMMM': {
regex: $locale.DATETIME_FORMATS.MONTH.join('|'),
apply: function(value) { this.month = $locale.DATETIME_FORMATS.MONTH.indexOf(value); }
},
'MMM': {
regex: $locale.DATETIME_FORMATS.SHORTMONTH.join('|'),
apply: function(value) { this.month = $locale.DATETIME_FORMATS.SHORTMONTH.indexOf(value); }
},
'MM': {
regex: '0[1-9]|1[0-2]',
apply: function(value) { this.month = value - 1; }
},
'M': {
regex: '[1-9]|1[0-2]',
apply: function(value) { this.month = value - 1; }
},
'dd': {
regex: '[0-2][0-9]{1}|3[0-1]{1}',
apply: function(value) { this.date = +value; }
},
'd': {
regex: '[1-2]?[0-9]{1}|3[0-1]{1}',
apply: function(value) { this.date = +value; }
},
'EEEE': {
regex: $locale.DATETIME_FORMATS.DAY.join('|')
},
'EEE': {
regex: $locale.DATETIME_FORMATS.SHORTDAY.join('|')
}
};
function createParser(format) {
var map = [], regex = format.split('');
angular.forEach(formatCodeToRegex, function(data, code) {
var index = format.indexOf(code);
if (index > -1) {
format = format.split('');
regex[index] = '(' + data.regex + ')';
format[index] = '$'; // Custom symbol to define consumed part of format
for (var i = index + 1, n = index + code.length; i < n; i++) {
regex[i] = '';
format[i] = '$';
}
format = format.join('');
map.push({ index: index, apply: data.apply });
}
});
return {
regex: new RegExp('^' + regex.join('') + '$'),
map: orderByFilter(map, 'index')
};
}
this.parse = function(input, format) {
if ( !angular.isString(input) || !format ) {
return input;
}
format = $locale.DATETIME_FORMATS[format] || format;
if ( !this.parsers[format] ) {
this.parsers[format] = createParser(format);
}
var parser = this.parsers[format],
regex = parser.regex,
map = parser.map,
results = input.match(regex);
if ( results && results.length ) {
var fields = { year: 1900, month: 0, date: 1, hours: 0 }, dt;
for( var i = 1, n = results.length; i < n; i++ ) {
var mapper = map[i-1];
if ( mapper.apply ) {
mapper.apply.call(fields, results[i]);
}
}
if ( isValid(fields.year, fields.month, fields.date) ) {
dt = new Date( fields.year, fields.month, fields.date, fields.hours);
}
return dt;
}
};
// Check if date is valid for specific month (and year for February).
// Month: 0 = Jan, 1 = Feb, etc
function isValid(year, month, date) {
if ( month === 1 && date > 28) {
return date === 29 && ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0);
}
if ( month === 3 || month === 5 || month === 8 || month === 10) {
return date < 31;
}
return true;
}
}]);
angular.module('ui.bootstrap.position', [])
/**
* A set of utility methods that can be use to retrieve position of DOM elements.
* It is meant to be used where we need to absolute-position DOM elements in
* relation to other, existing elements (this is the case for tooltips, popovers,
* typeahead suggestions etc.).
*/
.factory('$position', ['$document', '$window', function ($document, $window) {
function getStyle(el, cssprop) {
if (el.currentStyle) { //IE
return el.currentStyle[cssprop];
} else if ($window.getComputedStyle) {
return $window.getComputedStyle(el)[cssprop];
}
// finally try and get inline style
return el.style[cssprop];
}
/**
* Checks if a given element is statically positioned
* @param element - raw DOM element
*/
function isStaticPositioned(element) {
return (getStyle(element, 'position') || 'static' ) === 'static';
}
/**
* returns the closest, non-statically positioned parentOffset of a given element
* @param element
*/
var parentOffsetEl = function (element) {
var docDomEl = $document[0];
var offsetParent = element.offsetParent || docDomEl;
while (offsetParent && offsetParent !== docDomEl && isStaticPositioned(offsetParent) ) {
offsetParent = offsetParent.offsetParent;
}
return offsetParent || docDomEl;
};
return {
/**
* Provides read-only equivalent of jQuery's position function:
* http://api.jquery.com/position/
*/
position: function (element) {
var elBCR = this.offset(element);
var offsetParentBCR = { top: 0, left: 0 };
var offsetParentEl = parentOffsetEl(element[0]);
if (offsetParentEl != $document[0]) {
offsetParentBCR = this.offset(angular.element(offsetParentEl));
offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop;
offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft;
}
var boundingClientRect = element[0].getBoundingClientRect();
return {
width: boundingClientRect.width || element.prop('offsetWidth'),
height: boundingClientRect.height || element.prop('offsetHeight'),
top: elBCR.top - offsetParentBCR.top,
left: elBCR.left - offsetParentBCR.left
};
},
/**
* Provides read-only equivalent of jQuery's offset function:
* http://api.jquery.com/offset/
*/
offset: function (element) {
var boundingClientRect = element[0].getBoundingClientRect();
return {
width: boundingClientRect.width || element.prop('offsetWidth'),
height: boundingClientRect.height || element.prop('offsetHeight'),
top: boundingClientRect.top + ($window.pageYOffset || $document[0].documentElement.scrollTop),
left: boundingClientRect.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft)
};
},
/**
* Provides coordinates for the targetEl in relation to hostEl
*/
positionElements: function (hostEl, targetEl, positionStr, appendToBody) {
var positionStrParts = positionStr.split('-');
var pos0 = positionStrParts[0], pos1 = positionStrParts[1] || 'center';
var hostElPos,
targetElWidth,
targetElHeight,
targetElPos;
hostElPos = appendToBody ? this.offset(hostEl) : this.position(hostEl);
targetElWidth = targetEl.prop('offsetWidth');
targetElHeight = targetEl.prop('offsetHeight');
var shiftWidth = {
center: function () {
return hostElPos.left + hostElPos.width / 2 - targetElWidth / 2;
},
left: function () {
return hostElPos.left;
},
right: function () {
return hostElPos.left + hostElPos.width;
}
};
var shiftHeight = {
center: function () {
return hostElPos.top + hostElPos.height / 2 - targetElHeight / 2;
},
top: function () {
return hostElPos.top;
},
bottom: function () {
return hostElPos.top + hostElPos.height;
}
};
switch (pos0) {
case 'right':
targetElPos = {
top: shiftHeight[pos1](),
left: shiftWidth[pos0]()
};
break;
case 'left':
targetElPos = {
top: shiftHeight[pos1](),
left: hostElPos.left - targetElWidth
};
break;
case 'bottom':
targetElPos = {
top: shiftHeight[pos0](),
left: shiftWidth[pos1]()
};
break;
default:
targetElPos = {
top: hostElPos.top - targetElHeight,
left: shiftWidth[pos1]()
};
break;
}
return targetElPos;
}
};
}]);
<ul class="dropdown-menu" ng-style="{display: (isOpen && 'block') || 'none', top: position.top+'px', left: position.left+'px'}" ng-keydown="keydown($event)">
<li ng-transclude></li>
<li ng-if="showButtonBar" style="padding:10px 9px 2px">
<span class="btn-group">
<button type="button" class="btn btn-sm btn-info" ng-click="select('today')">{{ getText('current') }}</button>
<button type="button" class="btn btn-sm btn-danger" ng-click="select(null)">{{ getText('clear') }}</button>
</span>
<button type="button" class="btn btn-sm btn-success pull-right" ng-click="close()">{{ getText('close') }}</button>
</li>
</ul>
<div ng-switch="datepickerMode" role="application" ng-keydown="keydown($event)">
<daypicker ng-switch-when="day" tabindex="0"></daypicker>
<monthpicker ng-switch-when="month" tabindex="0"></monthpicker>
<yearpicker ng-switch-when="year" tabindex="0"></yearpicker>
</div>
<table role="grid" aria-labelledby="{{uniqueId}}-title" aria-activedescendant="{{activeDateId}}">
<thead>
<tr>
<th><button type="button" class="btn btn-default btn-sm pull-left" ng-click="move(-1)" tabindex="-1"><i class="glyphicon glyphicon-chevron-left"></i></button></th>
<th colspan="{{5 + showWeeks}}"><button id="{{uniqueId}}-title" role="heading" aria-live="assertive" aria-atomic="true" type="button" class="btn btn-default btn-sm" ng-click="toggleMode()" tabindex="-1" style="width:100%;"><strong>{{title}}</strong></button></th>
<th><button type="button" class="btn btn-default btn-sm pull-right" ng-click="move(1)" tabindex="-1"><i class="glyphicon glyphicon-chevron-right"></i></button></th>
</tr>
<tr>
<th ng-show="showWeeks" class="text-center"></th>
<th ng-repeat="label in labels track by $index" class="text-center"><small aria-label="{{label.full}}">{{label.abbr}}</small></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in rows track by $index">
<td ng-show="showWeeks" class="text-center h6"><em>{{ weekNumbers[$index] }}</em></td>
<td ng-repeat="dt in row track by dt.date" class="text-center" role="gridcell" id="{{dt.uid}}" aria-disabled="{{!!dt.disabled}}">
<button type="button" style="width:100%;" class="btn btn-sm" ng-class="{'btn-info': dt.selected, 'btn-success':!dt.disabled, 'btn-default': dt.disabled, active: isActive(dt)}"
ng-click="select(dt.date)" ng-disabled="dt.disabled" tabindex="-1">
<span ng-class="{'is-muted': dt.secondary, 'text-info': dt.current}">{{dt.label}}</span>
</button>
</td>
</tr>
</tbody>
</table>
<table role="grid" aria-labelledby="{{uniqueId}}-title" aria-activedescendant="{{activeDateId}}">
<thead>
<tr>
<th><button type="button" class="btn btn-default btn-sm pull-left" ng-click="move(-1)" tabindex="-1"><i class="glyphicon glyphicon-chevron-left"></i></button></th>
<th><button id="{{uniqueId}}-title" role="heading" aria-live="assertive" aria-atomic="true" type="button" class="btn btn-default btn-sm" ng-click="toggleMode()" tabindex="-1" style="width:100%;"><strong>{{title}}</strong></button></th>
<th><button type="button" class="btn btn-default btn-sm pull-right" ng-click="move(1)" tabindex="-1"><i class="glyphicon glyphicon-chevron-right"></i></button></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in rows track by $index">
<td ng-repeat="dt in row track by dt.date" class="text-center" role="gridcell" id="{{dt.uid}}" aria-disabled="{{!!dt.disabled}}">
<button type="button" style="width:100%;" class="btn btn-default" ng-class="{'btn-info': dt.selected, active: isActive(dt)}" ng-click="select(dt.date)" ng-disabled="dt.disabled" tabindex="-1"><span ng-class="{'text-info': dt.current}">{{dt.label}}</span></button>
</td>
</tr>
</tbody>
</table>
<table role="grid" aria-labelledby="{{uniqueId}}-title" aria-activedescendant="{{activeDateId}}">
<thead>
<tr>
<th><button type="button" class="btn btn-default btn-sm pull-left" ng-click="move(-1)" tabindex="-1"><i class="glyphicon glyphicon-chevron-left"></i></button></th>
<th colspan="3"><button id="{{uniqueId}}-title" role="heading" aria-live="assertive" aria-atomic="true" type="button" class="btn btn-default btn-sm" ng-click="toggleMode()" tabindex="-1" style="width:100%;"><strong>{{title}}</strong></button></th>
<th><button type="button" class="btn btn-default btn-sm pull-right" ng-click="move(1)" tabindex="-1"><i class="glyphicon glyphicon-chevron-right"></i></button></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in rows track by $index">
<td ng-repeat="dt in row track by dt.date" class="text-center" role="gridcell" id="{{dt.uid}}" aria-disabled="{{!!dt.disabled}}">
<button type="button" style="width:100%;" class="btn btn-default" ng-class="{'btn-info': dt.selected, active: isActive(dt)}" ng-click="select(dt.date)" ng-disabled="dt.disabled" tabindex="-1"><span ng-class="{'text-info': dt.current}">{{dt.label}}</span></button>
</td>
</tr>
</tbody>
</table>
[{
"timeZone": "Africa/Abidjan",
"offset": "0:00:00"
}, {
"timeZone": "Africa/Accra",
"offset": "0:00:00"
}, {
"timeZone": "Africa/Addis_Ababa",
"offset": "3:00:00"
}, {
"timeZone": "Africa/Algiers",
"offset": "1:00:00"
}, {
"timeZone": "Africa/Asmera",
"offset": "3:00:00"
}, {
"timeZone": "Africa/Bamako",
"offset": "0:00:00"
}, {
"timeZone": "Africa/Bangui",
"offset": "1:00:00"
}, {
"timeZone": "Africa/Banjul",
"offset": "0:00:00"
}, {
"timeZone": "Africa/Bissau",
"offset": "0:00:00"
}, {
"timeZone": "Africa/Blantyre",
"offset": "2:00:00"
}, {
"timeZone": "Africa/Brazzaville",
"offset": "1:00:00"
}, {
"timeZone": "Africa/Bujumbura",
"offset": "2:00:00"
}, {
"timeZone": "Africa/Cairo",
"offset": "2:00:00"
}, {
"timeZone": "Africa/Casablanca",
"offset": "0:00:00"
}, {
"timeZone": "Africa/Ceuta",
"offset": "1:00:00"
}, {
"timeZone": "Africa/Conakry",
"offset": "0:00:00"
}, {
"timeZone": "Africa/Dakar",
"offset": "0:00:00"
}, {
"timeZone": "Africa/Dar_es_Salaam",
"offset": "3:00:00"
}, {
"timeZone": "Africa/Djibouti",
"offset": "3:00:00"
}, {
"timeZone": "Africa/Douala",
"offset": "1:00:00"
}, {
"timeZone": "Africa/El_Aaiun",
"offset": "0:00:00"
}, {
"timeZone": "Africa/Freetown",
"offset": "0:00:00"
}, {
"timeZone": "Africa/Gaborone",
"offset": "2:00:00"
}, {
"timeZone": "Africa/Harare",
"offset": "2:00:00"
}, {
"timeZone": "Africa/Johannesburg",
"offset": "2:00:00"
}, {
"timeZone": "Africa/Juba",
"offset": "3:00:00"
}, {
"timeZone": "Africa/Kampala",
"offset": "3:00:00"
}, {
"timeZone": "Africa/Khartoum",
"offset": "3:00:00"
}, {
"timeZone": "Africa/Kigali",
"offset": "2:00:00"
}, {
"timeZone": "Africa/Kinshasa",
"offset": "1:00:00"
}, {
"timeZone": "Africa/Lagos",
"offset": "1:00:00"
}, {
"timeZone": "Africa/Libreville",
"offset": "1:00:00"
}, {
"timeZone": "Africa/Lome",
"offset": "0:00:00"
}, {
"timeZone": "Africa/Luanda",
"offset": "1:00:00"
}, {
"timeZone": "Africa/Lubumbashi",
"offset": "2:00:00"
}, {
"timeZone": "Africa/Lusaka",
"offset": "2:00:00"
}, {
"timeZone": "Africa/Malabo",
"offset": "1:00:00"
}, {
"timeZone": "Africa/Maputo",
"offset": "2:00:00"
}, {
"timeZone": "Africa/Maseru",
"offset": "2:00:00"
}, {
"timeZone": "Africa/Mbabane",
"offset": "2:00:00"
}, {
"timeZone": "Africa/Mogadishu",
"offset": "3:00:00"
}, {
"timeZone": "Africa/Monrovia",
"offset": "0:00:00"
}, {
"timeZone": "Africa/Nairobi",
"offset": "3:00:00"
}, {
"timeZone": "Africa/Ndjamena",
"offset": "1:00:00"
}, {
"timeZone": "Africa/Niamey",
"offset": "1:00:00"
}, {
"timeZone": "Africa/Nouakchott",
"offset": "0:00:00"
}, {
"timeZone": "Africa/Ouagadougou",
"offset": "0:00:00"
}, {
"timeZone": "Africa/Porto-Novo",
"offset": "1:00:00"
}, {
"timeZone": "Africa/Sao_Tome",
"offset": "0:00:00"
}, {
"timeZone": "Africa/Tripoli",
"offset": "2:00:00"
}, {
"timeZone": "Africa/Tunis",
"offset": "1:00:00"
}, {
"timeZone": "Africa/Windhoek",
"offset": "1:00:00"
}, {
"timeZone": "America/Anchorage",
"offset": "-9:00:00"
}, {
"timeZone": "America/Anguilla",
"offset": "-4:00:00"
}, {
"timeZone": "America/Antigua",
"offset": "-4:00:00"
}, {
"timeZone": "America/Araguaina",
"offset": "-3:00:00"
}, {
"timeZone": "America/Argentina/La_Rioja",
"offset": "-3:00:00"
}, {
"timeZone": "America/Argentina/Rio_Gallegos",
"offset": "-3:00:00"
}, {
"timeZone": "America/Argentina/Salta",
"offset": "-3:00:00"
}, {
"timeZone": "America/Argentina/San_Juan",
"offset": "-3:00:00"
}, {
"timeZone": "America/Argentina/San_Luis",
"offset": "-3:00:00"
}, {
"timeZone": "America/Argentina/Tucuman",
"offset": "-3:00:00"
}, {
"timeZone": "America/Argentina/Ushuaia",
"offset": "-3:00:00"
}, {
"timeZone": "America/Aruba",
"offset": "-4:00:00"
}, {
"timeZone": "America/Asuncion",
"offset": "-4:00:00"
}, {
"timeZone": "America/Bahia",
"offset": "-3:00:00"
}, {
"timeZone": "America/Bahia_Banderas",
"offset": "-6:00:00"
}, {
"timeZone": "America/Barbados",
"offset": "-4:00:00"
}, {
"timeZone": "America/Belem",
"offset": "-3:00:00"
}, {
"timeZone": "America/Belize",
"offset": "-6:00:00"
}, {
"timeZone": "America/Blanc-Sablon",
"offset": "-4:00:00"
}, {
"timeZone": "America/Boa_Vista",
"offset": "-4:00:00"
}, {
"timeZone": "America/Bogota",
"offset": "-5:00:00"
}, {
"timeZone": "America/Boise",
"offset": "-7:00:00"
}, {
"timeZone": "America/Buenos_Aires",
"offset": "-3:00:00"
}, {
"timeZone": "America/Cambridge_Bay",
"offset": "-7:00:00"
}, {
"timeZone": "America/Campo_Grande",
"offset": "-4:00:00"
}, {
"timeZone": "America/Cancun",
"offset": "-5:00:00"
}, {
"timeZone": "America/Caracas",
"offset": "-4:30:00"
}, {
"timeZone": "America/Catamarca",
"offset": "-3:00:00"
}, {
"timeZone": "America/Cayenne",
"offset": "-3:00:00"
}, {
"timeZone": "America/Cayman",
"offset": "-5:00:00"
}, {
"timeZone": "America/Chicago",
"offset": "-6:00:00"
}, {
"timeZone": "America/Chihuahua",
"offset": "-7:00:00"
}, {
"timeZone": "America/Coral_Harbour",
"offset": "-5:00:00"
}, {
"timeZone": "America/Cordoba",
"offset": "-3:00:00"
}, {
"timeZone": "America/Costa_Rica",
"offset": "-6:00:00"
}, {
"timeZone": "America/Creston",
"offset": "-7:00:00"
}, {
"timeZone": "America/Cuiaba",
"offset": "-4:00:00"
}, {
"timeZone": "America/Curacao",
"offset": "-4:00:00"
}, {
"timeZone": "America/Danmarkshavn",
"offset": "0:00:00"
}, {
"timeZone": "America/Dawson",
"offset": "-8:00:00"
}, {
"timeZone": "America/Dawson_Creek",
"offset": "-7:00:00"
}, {
"timeZone": "America/Denver",
"offset": "-7:00:00"
}, {
"timeZone": "America/Detroit",
"offset": "-5:00:00"
}, {
"timeZone": "America/Dominica",
"offset": "-4:00:00"
}, {
"timeZone": "America/Edmonton",
"offset": "-7:00:00"
}, {
"timeZone": "America/Eirunepe",
"offset": "-5:00:00"
}, {
"timeZone": "America/El_Salvador",
"offset": "-6:00:00"
}, {
"timeZone": "America/Fort_Nelson",
"offset": "-7:00:00"
}, {
"timeZone": "America/Fortaleza",
"offset": "-3:00:00"
}, {
"timeZone": "America/Glace_Bay",
"offset": "-4:00:00"
}, {
"timeZone": "America/Godthab",
"offset": "-3:00:00"
}, {
"timeZone": "America/Goose_Bay",
"offset": "-4:00:00"
}, {
"timeZone": "America/Grand_Turk",
"offset": "-4:00:00"
}, {
"timeZone": "America/Grenada",
"offset": "-4:00:00"
}, {
"timeZone": "America/Guadeloupe",
"offset": "-4:00:00"
}, {
"timeZone": "America/Guatemala",
"offset": "-6:00:00"
}, {
"timeZone": "America/Guayaquil",
"offset": "-5:00:00"
}, {
"timeZone": "America/Guyana",
"offset": "-4:00:00"
}, {
"timeZone": "America/Halifax",
"offset": "-4:00:00"
}, {
"timeZone": "America/Havana",
"offset": "-5:00:00"
}, {
"timeZone": "America/Hermosillo",
"offset": "-7:00:00"
}, {
"timeZone": "America/Indiana/Knox",
"offset": "-6:00:00"
}, {
"timeZone": "America/Indiana/Marengo",
"offset": "-5:00:00"
}, {
"timeZone": "America/Indiana/Petersburg",
"offset": "-5:00:00"
}, {
"timeZone": "America/Indiana/Tell_City",
"offset": "-6:00:00"
}, {
"timeZone": "America/Indiana/Vevay",
"offset": "-5:00:00"
}, {
"timeZone": "America/Indiana/Vincennes",
"offset": "-5:00:00"
}, {
"timeZone": "America/Indiana/Winamac",
"offset": "-5:00:00"
}, {
"timeZone": "America/Indianapolis",
"offset": "-5:00:00"
}, {
"timeZone": "America/Inuvik",
"offset": "-7:00:00"
}, {
"timeZone": "America/Iqaluit",
"offset": "-5:00:00"
}, {
"timeZone": "America/Jamaica",
"offset": "-5:00:00"
}, {
"timeZone": "America/Jujuy",
"offset": "-3:00:00"
}, {
"timeZone": "America/Juneau",
"offset": "-9:00:00"
}, {
"timeZone": "America/Kentucky/Monticello",
"offset": "-5:00:00"
}, {
"timeZone": "America/Kralendijk",
"offset": "-4:00:00"
}, {
"timeZone": "America/La_Paz",
"offset": "-4:00:00"
}, {
"timeZone": "America/Lima",
"offset": "-5:00:00"
}, {
"timeZone": "America/Los_Angeles",
"offset": "-8:00:00"
}, {
"timeZone": "America/Louisville",
"offset": "-5:00:00"
}, {
"timeZone": "America/Lower_Princes",
"offset": "-4:00:00"
}, {
"timeZone": "America/Maceio",
"offset": "-3:00:00"
}, {
"timeZone": "America/Managua",
"offset": "-6:00:00"
}, {
"timeZone": "America/Manaus",
"offset": "-4:00:00"
}, {
"timeZone": "America/Marigot",
"offset": "-4:00:00"
}, {
"timeZone": "America/Martinique",
"offset": "-4:00:00"
}, {
"timeZone": "America/Matamoros",
"offset": "-6:00:00"
}, {
"timeZone": "America/Mazatlan",
"offset": "-7:00:00"
}, {
"timeZone": "America/Mendoza",
"offset": "-3:00:00"
}, {
"timeZone": "America/Menominee",
"offset": "-6:00:00"
}, {
"timeZone": "America/Merida",
"offset": "-6:00:00"
}, {
"timeZone": "America/Metlakatla",
"offset": "-9:00:00"
}, {
"timeZone": "America/Mexico_City",
"offset": "-6:00:00"
}, {
"timeZone": "America/Moncton",
"offset": "-4:00:00"
}, {
"timeZone": "America/Monterrey",
"offset": "-6:00:00"
}, {
"timeZone": "America/Montevideo",
"offset": "-3:00:00"
}, {
"timeZone": "America/Montreal",
"offset": "-5:00:00"
}, {
"timeZone": "America/Montserrat",
"offset": "-4:00:00"
}, {
"timeZone": "America/Nassau",
"offset": "-5:00:00"
}, {
"timeZone": "America/New_York",
"offset": "-5:00:00"
}, {
"timeZone": "America/Nipigon",
"offset": "-5:00:00"
}, {
"timeZone": "America/Nome",
"offset": "-9:00:00"
}, {
"timeZone": "America/Noronha",
"offset": "-2:00:00"
}, {
"timeZone": "America/North_Dakota/Beulah",
"offset": "-6:00:00"
}, {
"timeZone": "America/North_Dakota/Center",
"offset": "-6:00:00"
}, {
"timeZone": "America/North_Dakota/New_Salem",
"offset": "-6:00:00"
}, {
"timeZone": "America/Ojinaga",
"offset": "-7:00:00"
}, {
"timeZone": "America/Panama",
"offset": "-5:00:00"
}, {
"timeZone": "America/Pangnirtung",
"offset": "-5:00:00"
}, {
"timeZone": "America/Paramaribo",
"offset": "-3:00:00"
}, {
"timeZone": "America/Phoenix",
"offset": "-7:00:00"
}, {
"timeZone": "America/Port_of_Spain",
"offset": "-4:00:00"
}, {
"timeZone": "America/Port-au-Prince",
"offset": "-5:00:00"
}, {
"timeZone": "America/Porto_Velho",
"offset": "-4:00:00"
}, {
"timeZone": "America/Puerto_Rico",
"offset": "-4:00:00"
}, {
"timeZone": "America/Rainy_River",
"offset": "-6:00:00"
}, {
"timeZone": "America/Rankin_Inlet",
"offset": "-6:00:00"
}, {
"timeZone": "America/Recife",
"offset": "-3:00:00"
}, {
"timeZone": "America/Regina",
"offset": "-6:00:00"
}, {
"timeZone": "America/Resolute",
"offset": "-6:00:00"
}, {
"timeZone": "America/Rio_Branco",
"offset": "-5:00:00"
}, {
"timeZone": "America/Santa_Isabel",
"offset": "-8:00:00"
}, {
"timeZone": "America/Santarem",
"offset": "-3:00:00"
}, {
"timeZone": "America/Santiago",
"offset": "-4:00:00"
}, {
"timeZone": "America/Santo_Domingo",
"offset": "-4:00:00"
}, {
"timeZone": "America/Sao_Paulo",
"offset": "-3:00:00"
}, {
"timeZone": "America/Scoresbysund",
"offset": "-1:00:00"
}, {
"timeZone": "America/Sitka",
"offset": "-9:00:00"
}, {
"timeZone": "America/St_Barthelemy",
"offset": "-4:00:00"
}, {
"timeZone": "America/St_Johns",
"offset": "-3:30:00"
}, {
"timeZone": "America/St_Kitts",
"offset": "-4:00:00"
}, {
"timeZone": "America/St_Lucia",
"offset": "-4:00:00"
}, {
"timeZone": "America/St_Thomas",
"offset": "-4:00:00"
}, {
"timeZone": "America/St_Vincent",
"offset": "-4:00:00"
}, {
"timeZone": "America/Swift_Current",
"offset": "-6:00:00"
}, {
"timeZone": "America/Tegucigalpa",
"offset": "-6:00:00"
}, {
"timeZone": "America/Thule",
"offset": "-4:00:00"
}, {
"timeZone": "America/Thunder_Bay",
"offset": "-5:00:00"
}, {
"timeZone": "America/Tijuana",
"offset": "-8:00:00"
}, {
"timeZone": "America/Toronto",
"offset": "-5:00:00"
}, {
"timeZone": "America/Tortola",
"offset": "-4:00:00"
}, {
"timeZone": "America/Vancouver",
"offset": "-8:00:00"
}, {
"timeZone": "America/Whitehorse",
"offset": "-8:00:00"
}, {
"timeZone": "America/Winnipeg",
"offset": "-6:00:00"
}, {
"timeZone": "America/Yakutat",
"offset": "-9:00:00"
}, {
"timeZone": "America/Yellowknife",
"offset": "-7:00:00"
}, {
"timeZone": "Antarctica/Casey",
"offset": "8:00:00"
}, {
"timeZone": "Antarctica/Davis",
"offset": "7:00:00"
}, {
"timeZone": "Antarctica/DumontDUrville",
"offset": "10:00:00"
}, {
"timeZone": "Antarctica/Macquarie",
"offset": "11:00:00"
}, {
"timeZone": "Antarctica/Mawson",
"offset": "5:00:00"
}, {
"timeZone": "Antarctica/McMurdo",
"offset": "12:00:00"
}, {
"timeZone": "Antarctica/Palmer",
"offset": "-4:00:00"
}, {
"timeZone": "Antarctica/Rothera",
"offset": "-3:00:00"
}, {
"timeZone": "Antarctica/Syowa",
"offset": "3:00:00"
}, {
"timeZone": "Antarctica/Vostok",
"offset": "6:00:00"
}, {
"timeZone": "Arctic/Longyearbyen",
"offset": "1:00:00"
}, {
"timeZone": "Asia/Aden",
"offset": "3:00:00"
}, {
"timeZone": "Asia/Almaty",
"offset": "6:00:00"
}, {
"timeZone": "Asia/Amman",
"offset": "2:00:00"
}, {
"timeZone": "Asia/Anadyr",
"offset": "12:00:00"
}, {
"timeZone": "Asia/Aqtau",
"offset": "5:00:00"
}, {
"timeZone": "Asia/Aqtobe",
"offset": "5:00:00"
}, {
"timeZone": "Asia/Ashgabat",
"offset": "5:00:00"
}, {
"timeZone": "Asia/Baghdad",
"offset": "3:00:00"
}, {
"timeZone": "Asia/Bahrain",
"offset": "3:00:00"
}, {
"timeZone": "Asia/Baku",
"offset": "4:00:00"
}, {
"timeZone": "Asia/Bangkok",
"offset": "7:00:00"
}, {
"timeZone": "Asia/Barnaul",
"offset": "6:00:00"
}, {
"timeZone": "Asia/Beirut",
"offset": "2:00:00"
}, {
"timeZone": "Asia/Bishkek",
"offset": "6:00:00"
}, {
"timeZone": "Asia/Brunei",
"offset": "8:00:00"
}, {
"timeZone": "Asia/Calcutta",
"offset": "5:30:00"
}, {
"timeZone": "Asia/Chita",
"offset": "8:00:00"
}, {
"timeZone": "Asia/Choibalsan",
"offset": "8:00:00"
}, {
"timeZone": "Asia/Colombo",
"offset": "5:30:00"
}, {
"timeZone": "Asia/Damascus",
"offset": "2:00:00"
}, {
"timeZone": "Asia/Dhaka",
"offset": "6:00:00"
}, {
"timeZone": "Asia/Dili",
"offset": "9:00:00"
}, {
"timeZone": "Asia/Dubai",
"offset": "4:00:00"
}, {
"timeZone": "Asia/Dushanbe",
"offset": "5:00:00"
}, {
"timeZone": "Asia/Hong_Kong",
"offset": "8:00:00"
}, {
"timeZone": "Asia/Irkutsk",
"offset": "8:00:00"
}, {
"timeZone": "Asia/Jakarta",
"offset": "7:00:00"
}, {
"timeZone": "Asia/Jayapura",
"offset": "9:00:00"
}, {
"timeZone": "Asia/Jerusalem",
"offset": "2:00:00"
}, {
"timeZone": "Asia/Kabul",
"offset": "4:30:00"
}, {
"timeZone": "Asia/Kamchatka",
"offset": "12:00:00"
}, {
"timeZone": "Asia/Karachi",
"offset": "5:00:00"
}, {
"timeZone": "Asia/Katmandu",
"offset": "5:45:00"
}, {
"timeZone": "Asia/Khandyga",
"offset": "9:00:00"
}, {
"timeZone": "Asia/Krasnoyarsk",
"offset": "7:00:00"
}, {
"timeZone": "Asia/Kuala_Lumpur",
"offset": "8:00:00"
}, {
"timeZone": "Asia/Kuching",
"offset": "8:00:00"
}, {
"timeZone": "Asia/Kuwait",
"offset": "3:00:00"
}, {
"timeZone": "Asia/Macau",
"offset": "8:00:00"
}, {
"timeZone": "Asia/Magadan",
"offset": "10:00:00"
}, {
"timeZone": "Asia/Makassar",
"offset": "8:00:00"
}, {
"timeZone": "Asia/Manila",
"offset": "8:00:00"
}, {
"timeZone": "Asia/Muscat",
"offset": "4:00:00"
}, {
"timeZone": "Asia/Nicosia",
"offset": "2:00:00"
}, {
"timeZone": "Asia/Novokuznetsk",
"offset": "7:00:00"
}, {
"timeZone": "Asia/Novosibirsk",
"offset": "6:00:00"
}, {
"timeZone": "Asia/Omsk",
"offset": "6:00:00"
}, {
"timeZone": "Asia/Oral",
"offset": "5:00:00"
}, {
"timeZone": "Asia/Phnom_Penh",
"offset": "7:00:00"
}, {
"timeZone": "Asia/Pontianak",
"offset": "7:00:00"
}, {
"timeZone": "Asia/Pyongyang",
"offset": "8:30:00"
}, {
"timeZone": "Asia/Qatar",
"offset": "3:00:00"
}, {
"timeZone": "Asia/Qyzylorda",
"offset": "6:00:00"
}, {
"timeZone": "Asia/Rangoon",
"offset": "6:30:00"
}, {
"timeZone": "Asia/Riyadh",
"offset": "3:00:00"
}, {
"timeZone": "Asia/Saigon",
"offset": "7:00:00"
}, {
"timeZone": "Asia/Sakhalin",
"offset": "10:00:00"
}, {
"timeZone": "Asia/Samarkand",
"offset": "5:00:00"
}, {
"timeZone": "Asia/Seoul",
"offset": "9:00:00"
}, {
"timeZone": "Asia/Shanghai",
"offset": "8:00:00"
}, {
"timeZone": "Asia/Singapore",
"offset": "8:00:00"
}, {
"timeZone": "Asia/Srednekolymsk",
"offset": "11:00:00"
}, {
"timeZone": "Asia/Taipei",
"offset": "8:00:00"
}, {
"timeZone": "Asia/Tashkent",
"offset": "5:00:00"
}, {
"timeZone": "Asia/Tbilisi",
"offset": "4:00:00"
}, {
"timeZone": "Asia/Tehran",
"offset": "3:30:00"
}, {
"timeZone": "Asia/Thimphu",
"offset": "6:00:00"
}, {
"timeZone": "Asia/Tokyo",
"offset": "9:00:00"
}, {
"timeZone": "Asia/Tomsk",
"offset": "6:00:00"
}, {
"timeZone": "Asia/Ulaanbaatar",
"offset": "8:00:00"
}, {
"timeZone": "Asia/Urumqi",
"offset": "6:00:00"
}, {
"timeZone": "Asia/Ust-Nera",
"offset": "10:00:00"
}, {
"timeZone": "Asia/Vientiane",
"offset": "7:00:00"
}, {
"timeZone": "Asia/Vladivostok",
"offset": "10:00:00"
}, {
"timeZone": "Asia/Yakutsk",
"offset": "9:00:00"
}, {
"timeZone": "Asia/Yekaterinburg",
"offset": "5:00:00"
}, {
"timeZone": "Asia/Yerevan",
"offset": "4:00:00"
}, {
"timeZone": "Atlantic/Azores",
"offset": "-1:00:00"
}, {
"timeZone": "Atlantic/Bermuda",
"offset": "-4:00:00"
}, {
"timeZone": "Atlantic/Canary",
"offset": "0:00:00"
}, {
"timeZone": "Atlantic/Cape_Verde",
"offset": "-1:00:00"
}, {
"timeZone": "Atlantic/Faeroe",
"offset": "0:00:00"
}, {
"timeZone": "Atlantic/Madeira",
"offset": "0:00:00"
}, {
"timeZone": "Atlantic/Reykjavik",
"offset": "0:00:00"
}, {
"timeZone": "Atlantic/South_Georgia",
"offset": "-2:00:00"
}, {
"timeZone": "Atlantic/St_Helena",
"offset": "0:00:00"
}, {
"timeZone": "Atlantic/Stanley",
"offset": "-3:00:00"
}, {
"timeZone": "Australia/Adelaide",
"offset": "9:30:00"
}, {
"timeZone": "Australia/Brisbane",
"offset": "10:00:00"
}, {
"timeZone": "Australia/Broken_Hill",
"offset": "9:30:00"
}, {
"timeZone": "Australia/Currie",
"offset": "10:00:00"
}, {
"timeZone": "Australia/Darwin",
"offset": "9:30:00"
}, {
"timeZone": "Australia/Hobart",
"offset": "10:00:00"
}, {
"timeZone": "Australia/Lindeman",
"offset": "10:00:00"
}, {
"timeZone": "Australia/Melbourne",
"offset": "10:00:00"
}, {
"timeZone": "Australia/Perth",
"offset": "8:00:00"
}, {
"timeZone": "Australia/Sydney",
"offset": "10:00:00"
}, {
"timeZone": "CST6CDT",
"offset": "-6:00:00"
}, {
"timeZone": "EST5EDT",
"offset": "-5:00:00"
}, {
"timeZone": "Etc/GMT",
"offset": "0:00:00"
}, {
"timeZone": "Etc/GMT+1",
"offset": "-1:00:00"
}, {
"timeZone": "Etc/GMT+10",
"offset": "-10:00:00"
}, {
"timeZone": "Etc/GMT+11",
"offset": "-11:00:00"
}, {
"timeZone": "Etc/GMT+12",
"offset": "-12:00:00"
}, {
"timeZone": "Etc/GMT+2",
"offset": "-2:00:00"
}, {
"timeZone": "Etc/GMT+3",
"offset": "-3:00:00"
}, {
"timeZone": "Etc/GMT+4",
"offset": "-4:00:00"
}, {
"timeZone": "Etc/GMT+5",
"offset": "-5:00:00"
}, {
"timeZone": "Etc/GMT+6",
"offset": "-6:00:00"
}, {
"timeZone": "Etc/GMT+7",
"offset": "-7:00:00"
}, {
"timeZone": "Etc/GMT-1",
"offset": "1:00:00"
}, {
"timeZone": "Etc/GMT-10",
"offset": "10:00:00"
}, {
"timeZone": "Etc/GMT-11",
"offset": "11:00:00"
}, {
"timeZone": "Etc/GMT-12",
"offset": "12:00:00"
}, {
"timeZone": "Etc/GMT-13",
"offset": "13:00:00"
}, {
"timeZone": "Etc/GMT-14",
"offset": "14:00:00"
}, {
"timeZone": "Etc/GMT-2",
"offset": "2:00:00"
}, {
"timeZone": "Etc/GMT-3",
"offset": "3:00:00"
}, {
"timeZone": "Etc/GMT-4",
"offset": "4:00:00"
}, {
"timeZone": "Etc/GMT-5",
"offset": "5:00:00"
}, {
"timeZone": "Etc/GMT-6",
"offset": "6:00:00"
}, {
"timeZone": "Etc/GMT-7",
"offset": "7:00:00"
}, {
"timeZone": "Etc/GMT-8",
"offset": "8:00:00"
}, {
"timeZone": "Etc/GMT-9",
"offset": "9:00:00"
}, {
"timeZone": "Europe/Amsterdam",
"offset": "1:00:00"
}, {
"timeZone": "Europe/Andorra",
"offset": "1:00:00"
}, {
"timeZone": "Europe/Astrakhan",
"offset": "3:00:00"
}, {
"timeZone": "Europe/Athens",
"offset": "2:00:00"
}, {
"timeZone": "Europe/Belgrade",
"offset": "1:00:00"
}, {
"timeZone": "Europe/Berlin",
"offset": "1:00:00"
}, {
"timeZone": "Europe/Bratislava",
"offset": "1:00:00"
}, {
"timeZone": "Europe/Brussels",
"offset": "1:00:00"
}, {
"timeZone": "Europe/Bucharest",
"offset": "2:00:00"
}, {
"timeZone": "Europe/Budapest",
"offset": "1:00:00"
}, {
"timeZone": "Europe/Busingen",
"offset": "1:00:00"
}, {
"timeZone": "Europe/Chisinau",
"offset": "2:00:00"
}, {
"timeZone": "Europe/Copenhagen",
"offset": "1:00:00"
}, {
"timeZone": "Europe/Dublin",
"offset": "0:00:00"
}, {
"timeZone": "Europe/Gibraltar",
"offset": "1:00:00"
}, {
"timeZone": "Europe/Guernsey",
"offset": "0:00:00"
}, {
"timeZone": "Europe/Helsinki",
"offset": "2:00:00"
}, {
"timeZone": "Europe/Isle_of_Man",
"offset": "0:00:00"
}, {
"timeZone": "Europe/Istanbul",
"offset": "2:00:00"
}, {
"timeZone": "Europe/Jersey",
"offset": "0:00:00"
}, {
"timeZone": "Europe/Kaliningrad",
"offset": "2:00:00"
}, {
"timeZone": "Europe/Kiev",
"offset": "2:00:00"
}, {
"timeZone": "Europe/Kirov",
"offset": "3:00:00"
}, {
"timeZone": "Europe/Lisbon",
"offset": "0:00:00"
}, {
"timeZone": "Europe/Ljubljana",
"offset": "1:00:00"
}, {
"timeZone": "Europe/London",
"offset": "0:00:00"
}, {
"timeZone": "Europe/Luxembourg",
"offset": "1:00:00"
}, {
"timeZone": "Europe/Madrid",
"offset": "1:00:00"
}, {
"timeZone": "Europe/Malta",
"offset": "1:00:00"
}, {
"timeZone": "Europe/Mariehamn",
"offset": "2:00:00"
}, {
"timeZone": "Europe/Minsk",
"offset": "3:00:00"
}, {
"timeZone": "Europe/Monaco",
"offset": "1:00:00"
}, {
"timeZone": "Europe/Moscow",
"offset": "3:00:00"
}, {
"timeZone": "Europe/Oslo",
"offset": "1:00:00"
}, {
"timeZone": "Europe/Paris",
"offset": "1:00:00"
}, {
"timeZone": "Europe/Podgorica",
"offset": "1:00:00"
}, {
"timeZone": "Europe/Prague",
"offset": "1:00:00"
}, {
"timeZone": "Europe/Riga",
"offset": "2:00:00"
}, {
"timeZone": "Europe/Rome",
"offset": "1:00:00"
}, {
"timeZone": "Europe/Samara",
"offset": "4:00:00"
}, {
"timeZone": "Europe/San_Marino",
"offset": "1:00:00"
}, {
"timeZone": "Europe/Sarajevo",
"offset": "1:00:00"
}, {
"timeZone": "Europe/Simferopol",
"offset": "3:00:00"
}, {
"timeZone": "Europe/Skopje",
"offset": "1:00:00"
}, {
"timeZone": "Europe/Sofia",
"offset": "2:00:00"
}, {
"timeZone": "Europe/Stockholm",
"offset": "1:00:00"
}, {
"timeZone": "Europe/Tallinn",
"offset": "2:00:00"
}, {
"timeZone": "Europe/Tirane",
"offset": "1:00:00"
}, {
"timeZone": "Europe/Ulyanovsk",
"offset": "3:00:00"
}, {
"timeZone": "Europe/Uzhgorod",
"offset": "2:00:00"
}, {
"timeZone": "Europe/Vaduz",
"offset": "1:00:00"
}, {
"timeZone": "Europe/Vatican",
"offset": "1:00:00"
}, {
"timeZone": "Europe/Vienna",
"offset": "1:00:00"
}, {
"timeZone": "Europe/Vilnius",
"offset": "2:00:00"
}, {
"timeZone": "Europe/Volgograd",
"offset": "3:00:00"
}, {
"timeZone": "Europe/Warsaw",
"offset": "1:00:00"
}, {
"timeZone": "Europe/Zagreb",
"offset": "1:00:00"
}, {
"timeZone": "Europe/Zaporozhye",
"offset": "2:00:00"
}, {
"timeZone": "Europe/Zurich",
"offset": "1:00:00"
}, {
"timeZone": "Indian/Antananarivo",
"offset": "3:00:00"
}, {
"timeZone": "Indian/Chagos",
"offset": "6:00:00"
}, {
"timeZone": "Indian/Christmas",
"offset": "7:00:00"
}, {
"timeZone": "Indian/Cocos",
"offset": "6:30:00"
}, {
"timeZone": "Indian/Comoro",
"offset": "3:00:00"
}, {
"timeZone": "Indian/Kerguelen",
"offset": "5:00:00"
}, {
"timeZone": "Indian/Mahe",
"offset": "4:00:00"
}, {
"timeZone": "Indian/Maldives",
"offset": "5:00:00"
}, {
"timeZone": "Indian/Mauritius",
"offset": "4:00:00"
}, {
"timeZone": "Indian/Mayotte",
"offset": "3:00:00"
}, {
"timeZone": "Indian/Reunion",
"offset": "4:00:00"
}, {
"timeZone": "MST7MDT",
"offset": "-7:00:00"
}, {
"timeZone": "Pacific/Apia",
"offset": "13:00:00"
}, {
"timeZone": "Pacific/Auckland",
"offset": "12:00:00"
}, {
"timeZone": "Pacific/Bougainville",
"offset": "11:00:00"
}, {
"timeZone": "Pacific/Easter",
"offset": "-5:00:00"
}, {
"timeZone": "Pacific/Efate",
"offset": "11:00:00"
}, {
"timeZone": "Pacific/Enderbury",
"offset": "13:00:00"
}, {
"timeZone": "Pacific/Fakaofo",
"offset": "13:00:00"
}, {
"timeZone": "Pacific/Fiji",
"offset": "12:00:00"
}, {
"timeZone": "Pacific/Funafuti",
"offset": "12:00:00"
}, {
"timeZone": "Pacific/Galapagos",
"offset": "-6:00:00"
}, {
"timeZone": "Pacific/Guadalcanal",
"offset": "11:00:00"
}, {
"timeZone": "Pacific/Guam",
"offset": "10:00:00"
}, {
"timeZone": "Pacific/Honolulu",
"offset": "-10:00:00"
}, {
"timeZone": "Pacific/Johnston",
"offset": "-10:00:00"
}, {
"timeZone": "Pacific/Kiritimati",
"offset": "14:00:00"
}, {
"timeZone": "Pacific/Kosrae",
"offset": "11:00:00"
}, {
"timeZone": "Pacific/Kwajalein",
"offset": "12:00:00"
}, {
"timeZone": "Pacific/Majuro",
"offset": "12:00:00"
}, {
"timeZone": "Pacific/Midway",
"offset": "-11:00:00"
}, {
"timeZone": "Pacific/Nauru",
"offset": "12:00:00"
}, {
"timeZone": "Pacific/Niue",
"offset": "-11:00:00"
}, {
"timeZone": "Pacific/Norfolk",
"offset": "11:00:00"
}, {
"timeZone": "Pacific/Noumea",
"offset": "11:00:00"
}, {
"timeZone": "Pacific/Pago_Pago",
"offset": "-11:00:00"
}, {
"timeZone": "Pacific/Palau",
"offset": "9:00:00"
}, {
"timeZone": "Pacific/Ponape",
"offset": "11:00:00"
}, {
"timeZone": "Pacific/Port_Moresby",
"offset": "10:00:00"
}, {
"timeZone": "Pacific/Rarotonga",
"offset": "-10:00:00"
}, {
"timeZone": "Pacific/Saipan",
"offset": "10:00:00"
}, {
"timeZone": "Pacific/Tahiti",
"offset": "-10:00:00"
}, {
"timeZone": "Pacific/Tarawa",
"offset": "12:00:00"
}, {
"timeZone": "Pacific/Tongatapu",
"offset": "13:00:00"
}, {
"timeZone": "Pacific/Truk",
"offset": "10:00:00"
}, {
"timeZone": "Pacific/Wake",
"offset": "12:00:00"
}, {
"timeZone": "Pacific/Wallis",
"offset": "12:00:00"
}, {
"timeZone": "PST8PDT",
"offset": "-8:00:00"
}]
const apiName = 'test.timezone.svc';
const tzCtrlName = 'TimeZoneCtrl';
if (!def) def = {};
if (!def.tz) def.tz = {};
def.tz.service = {
name: apiName,
dependencies: ['$http'],
definition: function($http) {
var success = function(response) {
if (response && response.data) {
return response.data;
}
return null;
};
var failure = function(error) {
console.error(error);
};
var api = {
getSupportedTimezones: function() {
return $http.get('api/timezones.json')
.then(success, failure);
}
};
return api;
}
};
def.tz.controller = {
name: tzCtrlName,
dependencies: [apiName],
definition: function(timeZoneSvc) {
var self = this;
self.tzData = {
all: [],
forCountry:undefined,
selected:undefined
};
Object.defineProperty(self, 'selectedTimeZone', {
get: function () {
if (self.tzData.selected) {
if (self.tzData.selected.region != 'Other') {
return self.tzData.selected.city + ', ' + self.tzData.selected.region;
} else {
return self.tzData.selected.city;
}
}
return 'Somewhere';
}
});
timeZoneSvc.getSupportedTimezones().then(function(data) {
if (data) {
var mapped = {};
for (var i = 0; i < data.length; i++) {
var tz = data[i];
var region = tz.timeZone.split('/');
if (region.length == 1 || region[0] == 'Etc') {
tz.region = 'Other';
} else {
tz.region = region[0];
}
if (region.length > 1) {
tz.city = region[1].replace(/_/g, ' ');
} else {
tz.city = region[0].replace(/_/g, ' ');
}
if (!mapped[tz.region]) {
mapped[tz.region] = {};
}
if (!mapped[tz.region][tz.city]) {
mapped[tz.region][tz.city] = tz;
}
}
self.tzData.all = mapped;
}
});
}
};
app.service(def.tz.service.name, def.tz.service.dependencies.concat(def.tz.service.definition));
app.controller(def.tz.controller.name, def.tz.controller.dependencies.concat(def.tz.controller.definition));