1176 lines
46 KiB
Python
1176 lines
46 KiB
Python
(function (global, factory) {'use strict';var fnc;fnc = (typeof exports === 'object' && typeof module !== 'undefined') ? module.exports = factory(require('angular'), require('moment')) :(typeof define === 'function' && define.amd) ? define(['angular', 'moment'], factory) :factory(global.angular, global.moment);}(this, function (angular, moment) {
|
|
//(function (global, factory) {
|
|
// 'use strict';
|
|
// var fnc;
|
|
// fnc = (typeof exports === 'object' && typeof module !== 'undefined') ? module.exports = factory(require('angular'), require('moment')) :
|
|
// (typeof define === 'function' && define.amd) ? define(['angular', 'moment'], factory) :
|
|
// factory(global.angular, global.moment);
|
|
//}(this, function (angular, moment) {
|
|
var Module = angular.module('datePicker', []);
|
|
|
|
Module.constant('datePickerConfig', {
|
|
template: 'templates/datepicker.html',
|
|
view: 'month',
|
|
views: ['year', 'month', 'date', 'hours', 'minutes'],
|
|
momentNames: {
|
|
year: 'year',
|
|
month: 'month',
|
|
date: 'day',
|
|
hours: 'hours',
|
|
minutes: 'minutes',
|
|
},
|
|
viewConfig: {
|
|
year: ['years', 'isSameYear'],
|
|
month: ['months', 'isSameMonth'],
|
|
hours: ['hours', 'isSameHour'],
|
|
minutes: ['minutes', 'isSameMinutes'],
|
|
},
|
|
step: 5
|
|
});
|
|
|
|
//Moment format filter.
|
|
Module.filter('mFormat', function () {
|
|
return function (m, format, tz) {
|
|
if (!(moment.isMoment(m))) {
|
|
return moment(m).format(format);
|
|
}
|
|
return tz ? moment.tz(m, tz).format(format) : m.format(format);
|
|
};
|
|
});
|
|
|
|
Module.directive('datePicker', ['datePickerConfig', 'datePickerUtils', function datePickerDirective(datePickerConfig, datePickerUtils) {
|
|
|
|
//noinspection JSUnusedLocalSymbols
|
|
return {
|
|
// this is a bug ?
|
|
require: '?ngModel',
|
|
template: '<div ng-include="template"></div>',
|
|
scope: {
|
|
model: '=datePicker',
|
|
after: '=?',
|
|
before: '=?'
|
|
},
|
|
link: function (scope, element, attrs, ngModel) {
|
|
function prepareViews() {
|
|
scope.views = datePickerConfig.views.concat();
|
|
scope.view = attrs.view || datePickerConfig.view;
|
|
|
|
scope.views = scope.views.slice(
|
|
scope.views.indexOf(attrs.maxView || 'year'),
|
|
scope.views.indexOf(attrs.minView || 'minutes') + 1
|
|
);
|
|
|
|
if (scope.views.length === 1 || scope.views.indexOf(scope.view) === -1) {
|
|
scope.view = scope.views[0];
|
|
}
|
|
}
|
|
|
|
function getDate(name) {
|
|
return datePickerUtils.getDate(scope, attrs, name);
|
|
}
|
|
|
|
var arrowClick = false,
|
|
tz = scope.tz = attrs.timezone,
|
|
createMoment = datePickerUtils.createMoment,
|
|
eventIsForPicker = datePickerUtils.eventIsForPicker,
|
|
step = parseInt(attrs.step || datePickerConfig.step, 10),
|
|
partial = !!attrs.partial,
|
|
minDate = getDate('minDate'),
|
|
maxDate = getDate('maxDate'),
|
|
pickerID = element[0].id,
|
|
now = scope.now = createMoment(),
|
|
selected = scope.date = createMoment(scope.model || now),
|
|
autoclose = attrs.autoClose === 'true',
|
|
// Either gets the 1st day from the attributes, or asks moment.js to give it to us as it is localized.
|
|
firstDay = attrs.firstDay && attrs.firstDay >= 0 && attrs.firstDay <= 6 ? parseInt(attrs.firstDay, 10) : moment().weekday(0).day(),
|
|
setDate,
|
|
prepareViewData,
|
|
isSame,
|
|
clipDate,
|
|
isNow,
|
|
inValidRange;
|
|
|
|
datePickerUtils.setParams(tz, firstDay);
|
|
|
|
if (!scope.model) {
|
|
selected.minute(Math.ceil(selected.minute() / step) * step).second(0);
|
|
}
|
|
|
|
scope.template = attrs.template || datePickerConfig.template;
|
|
|
|
scope.watchDirectChanges = attrs.watchDirectChanges !== undefined;
|
|
scope.callbackOnSetDate = attrs.dateChange ? datePickerUtils.findFunction(scope, attrs.dateChange) : undefined;
|
|
|
|
prepareViews();
|
|
|
|
scope.setView = function (nextView) {
|
|
if (scope.views.indexOf(nextView) !== -1) {
|
|
scope.view = nextView;
|
|
}
|
|
};
|
|
|
|
scope.selectDate = function (date) {
|
|
if (attrs.disabled) {
|
|
return false;
|
|
}
|
|
if (isSame(scope.date, date)) {
|
|
date = scope.date;
|
|
}
|
|
date = clipDate(date);
|
|
if (!date) {
|
|
return false;
|
|
}
|
|
scope.date = date;
|
|
|
|
var nextView = scope.views[scope.views.indexOf(scope.view) + 1];
|
|
if ((!nextView || partial) || scope.model) {
|
|
setDate(date);
|
|
}
|
|
|
|
if (nextView) {
|
|
scope.setView(nextView);
|
|
} else if (autoclose) {
|
|
element.addClass('hidden');
|
|
scope.$emit('hidePicker');
|
|
} else {
|
|
prepareViewData();
|
|
}
|
|
};
|
|
|
|
setDate = function (date) {
|
|
if (date) {
|
|
scope.model = date;
|
|
if (ngModel) {
|
|
ngModel.$setViewValue(date);
|
|
}
|
|
}
|
|
scope.$emit('setDate', scope.model, scope.view);
|
|
|
|
//This is duplicated in the new functionality.
|
|
if (scope.callbackOnSetDate) {
|
|
scope.callbackOnSetDate(attrs.datePicker, scope.date);
|
|
}
|
|
};
|
|
|
|
function update() {
|
|
var view = scope.view;
|
|
datePickerUtils.setParams(tz, firstDay);
|
|
|
|
if (scope.model && !arrowClick) {
|
|
scope.date = createMoment(scope.model);
|
|
arrowClick = false;
|
|
}
|
|
|
|
var date = scope.date;
|
|
|
|
switch (view) {
|
|
case 'year':
|
|
scope.years = datePickerUtils.getVisibleYears(date);
|
|
break;
|
|
case 'month':
|
|
scope.months = datePickerUtils.getVisibleMonths(date);
|
|
break;
|
|
case 'date':
|
|
scope.weekdays = scope.weekdays || datePickerUtils.getDaysOfWeek();
|
|
scope.weeks = datePickerUtils.getVisibleWeeks(date);
|
|
break;
|
|
case 'hours':
|
|
scope.hours = datePickerUtils.getVisibleHours(date);
|
|
break;
|
|
case 'minutes':
|
|
scope.minutes = datePickerUtils.getVisibleMinutes(date, step);
|
|
break;
|
|
}
|
|
|
|
prepareViewData();
|
|
}
|
|
|
|
function watch() {
|
|
if (scope.view !== 'date') {
|
|
return scope.view;
|
|
}
|
|
return scope.date ? scope.date.month() : null;
|
|
}
|
|
|
|
scope.$watch(watch, update);
|
|
|
|
if (scope.watchDirectChanges) {
|
|
scope.$watch('model', function () {
|
|
arrowClick = false;
|
|
update();
|
|
});
|
|
}
|
|
|
|
prepareViewData = function () {
|
|
var view = scope.view,
|
|
date = scope.date,
|
|
classes = [], classList = '',
|
|
i, j;
|
|
|
|
datePickerUtils.setParams(tz, firstDay);
|
|
|
|
if (view === 'date') {
|
|
var weeks = scope.weeks, week;
|
|
for (i = 0; i < weeks.length; i++) {
|
|
week = weeks[i];
|
|
classes.push([]);
|
|
for (j = 0; j < week.length; j++) {
|
|
classList = '';
|
|
if (datePickerUtils.isSameDay(date, week[j])) {
|
|
classList += 'active';
|
|
}
|
|
if (isNow(week[j], view)) {
|
|
classList += ' now';
|
|
}
|
|
//if (week[j].month() !== date.month()) classList += ' disabled';
|
|
if (week[j].month() !== date.month() || !inValidRange(week[j])) {
|
|
classList += ' disabled';
|
|
}
|
|
classes[i].push(classList);
|
|
}
|
|
}
|
|
} else {
|
|
var params = datePickerConfig.viewConfig[view],
|
|
dates = scope[params[0]],
|
|
compareFunc = params[1];
|
|
|
|
for (i = 0; i < dates.length; i++) {
|
|
classList = '';
|
|
if (datePickerUtils[compareFunc](date, dates[i])) {
|
|
classList += 'active';
|
|
}
|
|
if (isNow(dates[i], view)) {
|
|
classList += ' now';
|
|
}
|
|
if (!inValidRange(dates[i])) {
|
|
classList += ' disabled';
|
|
}
|
|
classes.push(classList);
|
|
}
|
|
}
|
|
scope.classes = classes;
|
|
};
|
|
|
|
scope.next = function (delta) {
|
|
var date = moment(scope.date);
|
|
delta = delta || 1;
|
|
switch (scope.view) {
|
|
case 'year':
|
|
/*falls through*/
|
|
case 'month':
|
|
date.year(date.year() + delta);
|
|
break;
|
|
case 'date':
|
|
date.month(date.month() + delta);
|
|
break;
|
|
case 'hours':
|
|
/*falls through*/
|
|
case 'minutes':
|
|
date.hours(date.hours() + delta);
|
|
break;
|
|
}
|
|
date = clipDate(date);
|
|
if (date) {
|
|
scope.date = date;
|
|
setDate(date);
|
|
arrowClick = true;
|
|
update();
|
|
}
|
|
};
|
|
|
|
inValidRange = function (date) {
|
|
var valid = true;
|
|
if (minDate && minDate.isAfter(date)) {
|
|
valid = isSame(minDate, date);
|
|
}
|
|
if (maxDate && maxDate.isBefore(date)) {
|
|
valid &= isSame(maxDate, date);
|
|
}
|
|
return valid;
|
|
};
|
|
|
|
isSame = function (date1, date2) {
|
|
return date1.isSame(date2, datePickerConfig.momentNames[scope.view]) ? true : false;
|
|
};
|
|
|
|
clipDate = function (date) {
|
|
if (minDate && minDate.isAfter(date)) {
|
|
return minDate;
|
|
} else if (maxDate && maxDate.isBefore(date)) {
|
|
return maxDate;
|
|
} else {
|
|
return date;
|
|
}
|
|
};
|
|
|
|
isNow = function (date, view) {
|
|
var is = true;
|
|
|
|
switch (view) {
|
|
case 'minutes':
|
|
is &= ~~(now.minutes() / step) === ~~(date.minutes() / step);
|
|
/* falls through */
|
|
case 'hours':
|
|
is &= now.hours() === date.hours();
|
|
/* falls through */
|
|
case 'date':
|
|
is &= now.date() === date.date();
|
|
/* falls through */
|
|
case 'month':
|
|
is &= now.month() === date.month();
|
|
/* falls through */
|
|
case 'year':
|
|
is &= now.year() === date.year();
|
|
}
|
|
return is;
|
|
};
|
|
|
|
scope.prev = function (delta) {
|
|
return scope.next(-delta || -1);
|
|
};
|
|
|
|
if (pickerID) {
|
|
scope.$on('pickerUpdate', function (event, pickerIDs, data) {
|
|
if (eventIsForPicker(pickerIDs, pickerID)) {
|
|
var updateViews = false, updateViewData = false;
|
|
|
|
if (angular.isDefined(data.minDate)) {
|
|
minDate = data.minDate ? data.minDate : false;
|
|
updateViewData = true;
|
|
}
|
|
if (angular.isDefined(data.maxDate)) {
|
|
maxDate = data.maxDate ? data.maxDate : false;
|
|
updateViewData = true;
|
|
}
|
|
|
|
if (angular.isDefined(data.minView)) {
|
|
attrs.minView = data.minView;
|
|
updateViews = true;
|
|
}
|
|
if (angular.isDefined(data.maxView)) {
|
|
attrs.maxView = data.maxView;
|
|
updateViews = true;
|
|
}
|
|
attrs.view = data.view || attrs.view;
|
|
|
|
if (updateViews) {
|
|
prepareViews();
|
|
}
|
|
|
|
if (updateViewData) {
|
|
update();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
};
|
|
}]);
|
|
//}));
|
|
|
|
//(function (global, factory) {
|
|
// 'use strict';
|
|
// var fnc;
|
|
// fnc = (typeof exports === 'object' && typeof module !== 'undefined') ? module.exports = factory(require('angular'), require('moment')) :
|
|
// (typeof define === 'function' && define.amd) ? define(['angular', 'moment'], factory) :
|
|
// factory(global.angular, global.moment);
|
|
//}(this, function (angular, moment) {
|
|
angular.module('datePicker').factory('datePickerUtils', function () {
|
|
var tz, firstDay;
|
|
var createNewDate = function (year, month, day, hour, minute) {
|
|
var utc = Date.UTC(year | 0, month | 0, day | 0, hour | 0, minute | 0);
|
|
return tz ? moment.tz(utc, tz) : moment(utc);
|
|
};
|
|
|
|
return {
|
|
getVisibleMinutes: function (m, step) {
|
|
var year = m.year(),
|
|
month = m.month(),
|
|
day = m.date(),
|
|
hour = m.hours(), pushedDate,
|
|
offset = m.utcOffset() / 60,
|
|
minutes = [], minute;
|
|
|
|
for (minute = 0; minute < 60; minute += step) {
|
|
pushedDate = createNewDate(year, month, day, hour - offset, minute);
|
|
minutes.push(pushedDate);
|
|
}
|
|
return minutes;
|
|
},
|
|
getVisibleWeeks: function (m) {
|
|
m = moment(m);
|
|
var startYear = m.year(),
|
|
startMonth = m.month();
|
|
|
|
//Set date to the first day of the month
|
|
m.date(1);
|
|
|
|
//Grab day of the week
|
|
var day = m.day();
|
|
|
|
//Go back the required number of days to arrive at the previous week start
|
|
m.date(firstDay - (day + (firstDay >= day ? 6 : -1)));
|
|
|
|
var weeks = [];
|
|
|
|
while (weeks.length < 6) {
|
|
if (m.year() === startYear && m.month() > startMonth) {
|
|
break;
|
|
}
|
|
weeks.push(this.getDaysOfWeek(m));
|
|
m.add(7, 'd');
|
|
}
|
|
return weeks;
|
|
},
|
|
getVisibleYears: function (d) {
|
|
var m = moment(d),
|
|
year = m.year();
|
|
|
|
m.year(year - (year % 10));
|
|
year = m.year();
|
|
|
|
var offset = m.utcOffset() / 60,
|
|
years = [],
|
|
pushedDate,
|
|
actualOffset;
|
|
|
|
for (var i = 0; i < 12; i++) {
|
|
pushedDate = createNewDate(year, 0, 1, 0 - offset);
|
|
actualOffset = pushedDate.utcOffset() / 60;
|
|
if (actualOffset !== offset) {
|
|
pushedDate = createNewDate(year, 0, 1, 0 - actualOffset);
|
|
offset = actualOffset;
|
|
}
|
|
years.push(pushedDate);
|
|
year++;
|
|
}
|
|
return years;
|
|
},
|
|
getDaysOfWeek: function (m) {
|
|
m = m ? m : (tz ? moment.tz(tz).day(firstDay) : moment().day(firstDay));
|
|
|
|
var year = m.year(),
|
|
month = m.month(),
|
|
day = m.date(),
|
|
days = [],
|
|
pushedDate,
|
|
offset = m.utcOffset() / 60,
|
|
actualOffset;
|
|
|
|
for (var i = 0; i < 7; i++) {
|
|
pushedDate = createNewDate(year, month, day, 0 - offset, 0, false);
|
|
actualOffset = pushedDate.utcOffset() / 60;
|
|
if (actualOffset !== offset) {
|
|
pushedDate = createNewDate(year, month, day, 0 - actualOffset, 0, false);
|
|
}
|
|
days.push(pushedDate);
|
|
day++;
|
|
}
|
|
return days;
|
|
},
|
|
getVisibleMonths: function (m) {
|
|
var year = m.year(),
|
|
offset = m.utcOffset() / 60,
|
|
months = [],
|
|
pushedDate,
|
|
actualOffset;
|
|
|
|
for (var month = 0; month < 12; month++) {
|
|
pushedDate = createNewDate(year, month, 1, 0 - offset, 0, false);
|
|
actualOffset = pushedDate.utcOffset() / 60;
|
|
if (actualOffset !== offset) {
|
|
pushedDate = createNewDate(year, month, 1, 0 - actualOffset, 0, false);
|
|
}
|
|
months.push(pushedDate);
|
|
}
|
|
return months;
|
|
},
|
|
getVisibleHours: function (m) {
|
|
var year = m.year(),
|
|
month = m.month(),
|
|
day = m.date(),
|
|
hours = [],
|
|
hour, pushedDate, actualOffset,
|
|
offset = m.utcOffset() / 60;
|
|
|
|
for (hour = 0; hour < 24; hour++) {
|
|
pushedDate = createNewDate(year, month, day, hour - offset, 0, false);
|
|
actualOffset = pushedDate.utcOffset() / 60;
|
|
if (actualOffset !== offset) {
|
|
pushedDate = createNewDate(year, month, day, hour - actualOffset, 0, false);
|
|
}
|
|
hours.push(pushedDate);
|
|
}
|
|
|
|
return hours;
|
|
},
|
|
isAfter: function (model, date) {
|
|
return model && model.unix() >= date.unix();
|
|
},
|
|
isBefore: function (model, date) {
|
|
return model.unix() <= date.unix();
|
|
},
|
|
isSameYear: function (model, date) {
|
|
return model && model.year() === date.year();
|
|
},
|
|
isSameMonth: function (model, date) {
|
|
return this.isSameYear(model, date) && model.month() === date.month();
|
|
},
|
|
isSameDay: function (model, date) {
|
|
return this.isSameMonth(model, date) && model.date() === date.date();
|
|
},
|
|
isSameHour: function (model, date) {
|
|
return this.isSameDay(model, date) && model.hours() === date.hours();
|
|
},
|
|
isSameMinutes: function (model, date) {
|
|
return this.isSameHour(model, date) && model.minutes() === date.minutes();
|
|
},
|
|
setParams: function (zone, fd) {
|
|
tz = zone;
|
|
firstDay = fd;
|
|
},
|
|
scopeSearch: function (scope, name, comparisonFn) {
|
|
var parentScope = scope,
|
|
nameArray = name.split('.'),
|
|
target, i, j = nameArray.length;
|
|
|
|
do {
|
|
target = parentScope = parentScope.$parent;
|
|
|
|
//Loop through provided names.
|
|
for (i = 0; i < j; i++) {
|
|
target = target[nameArray[i]];
|
|
if (!target) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//If we reached the end of the list for this scope,
|
|
//and something was found, trigger the comparison
|
|
//function. If the comparison function is happy, return
|
|
//found result. Otherwise, continue to the next parent scope
|
|
if (target && comparisonFn(target)) {
|
|
return target;
|
|
}
|
|
|
|
} while (parentScope.$parent);
|
|
|
|
return false;
|
|
},
|
|
findFunction: function (scope, name) {
|
|
//Search scope ancestors for a matching function.
|
|
return this.scopeSearch(scope, name, function (target) {
|
|
//Property must also be a function
|
|
return angular.isFunction(target);
|
|
});
|
|
},
|
|
findParam: function (scope, name) {
|
|
//Search scope ancestors for a matching parameter.
|
|
return this.scopeSearch(scope, name, function () {
|
|
//As long as the property exists, we're good
|
|
return true;
|
|
});
|
|
},
|
|
createMoment: function (m) {
|
|
if (tz) {
|
|
return moment.tz(m, tz);
|
|
} else {
|
|
//If input is a moment, and we have no TZ info, we need to remove TZ
|
|
//info from the moment, otherwise the newly created moment will take
|
|
//the timezone of the input moment. The easiest way to do that is to
|
|
//take the unix timestamp, and use that to create a new moment.
|
|
//The new moment will use the local timezone of the user machine.
|
|
return moment.isMoment(m) ? moment.unix(m.unix()) : moment(m);
|
|
}
|
|
},
|
|
getDate: function (scope, attrs, name) {
|
|
var result = false;
|
|
if (attrs[name]) {
|
|
result = this.createMoment(attrs[name]);
|
|
if (!result.isValid()) {
|
|
result = this.findParam(scope, attrs[name]);
|
|
if (result) {
|
|
result = this.createMoment(result);
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
},
|
|
eventIsForPicker: function (targetIDs, pickerID) {
|
|
//Checks if an event targeted at a specific picker, via either a string name, or an array of strings.
|
|
return (angular.isArray(targetIDs) && targetIDs.indexOf(pickerID) > -1 || targetIDs === pickerID);
|
|
}
|
|
};
|
|
});
|
|
//}));
|
|
|
|
//(function (global, factory) {
|
|
// 'use strict';
|
|
// var fnc;
|
|
// fnc = (typeof exports === 'object' && typeof module !== 'undefined') ? module.exports = factory(require('angular'), require('moment')) :
|
|
// (typeof define === 'function' && define.amd) ? define(['angular', 'moment'], factory) :
|
|
// factory(global.angular, global.moment);
|
|
//}(this, function (angular, moment) {
|
|
var Module = angular.module('datePicker');
|
|
|
|
Module.directive('dateRange', ['$compile', 'datePickerUtils', 'dateTimeConfig', function ($compile, datePickerUtils, dateTimeConfig) {
|
|
function getTemplate(attrs, id, model, min, max) {
|
|
return dateTimeConfig.template(angular.extend(attrs, {
|
|
ngModel: model,
|
|
minDate: min && moment.isMoment(min) ? min.format() : false,
|
|
maxDate: max && moment.isMoment(max) ? max.format() : false
|
|
}), id);
|
|
}
|
|
|
|
function randomName() {
|
|
return 'picker' + Math.random().toString().substr(2);
|
|
}
|
|
|
|
return {
|
|
scope: {
|
|
start: '=',
|
|
end: '='
|
|
},
|
|
link: function (scope, element, attrs) {
|
|
var dateChange = null,
|
|
pickerRangeID = element[0].id,
|
|
pickerIDs = [randomName(), randomName()],
|
|
createMoment = datePickerUtils.createMoment,
|
|
eventIsForPicker = datePickerUtils.eventIsForPicker;
|
|
|
|
scope.dateChange = function (modelName, newDate) {
|
|
//Notify user if callback exists.
|
|
if (dateChange) {
|
|
dateChange(modelName, newDate);
|
|
}
|
|
};
|
|
|
|
function setMax(date) {
|
|
scope.$broadcast('pickerUpdate', pickerIDs[0], {
|
|
maxDate: date
|
|
});
|
|
}
|
|
|
|
function setMin(date) {
|
|
scope.$broadcast('pickerUpdate', pickerIDs[1], {
|
|
minDate: date
|
|
});
|
|
}
|
|
|
|
if (pickerRangeID) {
|
|
scope.$on('pickerUpdate', function (event, targetIDs, data) {
|
|
if (eventIsForPicker(targetIDs, pickerRangeID)) {
|
|
//If we received an update event, dispatch it to the inner pickers using their IDs.
|
|
scope.$broadcast('pickerUpdate', pickerIDs, data);
|
|
}
|
|
});
|
|
}
|
|
|
|
datePickerUtils.setParams(attrs.timezone);
|
|
|
|
scope.start = createMoment(scope.start);
|
|
scope.end = createMoment(scope.end);
|
|
|
|
scope.$watchGroup(['start', 'end'], function (dates) {
|
|
//Scope data changed, update picker min/max
|
|
setMin(dates[0]);
|
|
setMax(dates[1]);
|
|
});
|
|
|
|
if (angular.isDefined(attrs.dateChange)) {
|
|
dateChange = datePickerUtils.findFunction(scope, attrs.dateChange);
|
|
}
|
|
|
|
attrs.onSetDate = 'dateChange';
|
|
|
|
var template = '<div><table class="date-range"><tr><td valign="top">' +
|
|
getTemplate(attrs, pickerIDs[0], 'start', false, scope.end) +
|
|
'</td><td valign="top">' +
|
|
getTemplate(attrs, pickerIDs[1], 'end', scope.start, false) +
|
|
'</td></tr></table></div>';
|
|
|
|
var picker = $compile(template)(scope);
|
|
element.append(picker);
|
|
}
|
|
};
|
|
}]);
|
|
//}));
|
|
|
|
//(function (global, factory) {
|
|
// 'use strict';
|
|
// var fnc;
|
|
// fnc = (typeof exports === 'object' && typeof module !== 'undefined') ? module.exports = factory(require('angular'), require('moment')) :
|
|
// (typeof define === 'function' && define.amd) ? define(['angular', 'moment'], factory) :
|
|
// factory(global.angular, global.moment);
|
|
//}(this, function (angular, moment) {
|
|
var PRISTINE_CLASS = 'ng-pristine',
|
|
DIRTY_CLASS = 'ng-dirty';
|
|
|
|
var Module = angular.module('datePicker');
|
|
|
|
Module.constant('dateTimeConfig', {
|
|
template: function (attrs, id) {
|
|
return '' +
|
|
'<div ' +
|
|
(id ? 'id="' + id + '" ' : '') +
|
|
'date-picker="' + attrs.ngModel + '" ' +
|
|
(attrs.view ? 'view="' + attrs.view + '" ' : '') +
|
|
(attrs.maxView ? 'max-view="' + attrs.maxView + '" ' : '') +
|
|
(attrs.maxDate ? 'max-date="' + attrs.maxDate + '" ' : '') +
|
|
(attrs.autoClose ? 'auto-close="' + attrs.autoClose + '" ' : '') +
|
|
(attrs.template ? 'template="' + attrs.template + '" ' : '') +
|
|
(attrs.minView ? 'min-view="' + attrs.minView + '" ' : '') +
|
|
(attrs.minDate ? 'min-date="' + attrs.minDate + '" ' : '') +
|
|
(attrs.partial ? 'partial="' + attrs.partial + '" ' : '') +
|
|
(attrs.step ? 'step="' + attrs.step + '" ' : '') +
|
|
(attrs.onSetDate ? 'date-change="' + attrs.onSetDate + '" ' : '') +
|
|
(attrs.ngModel ? 'ng-model="' + attrs.ngModel + '" ' : '') +
|
|
(attrs.firstDay ? 'first-day="' + attrs.firstDay + '" ' : '') +
|
|
(attrs.timezone ? 'timezone="' + attrs.timezone + '" ' : '') +
|
|
'class="date-picker-date-time"></div>';
|
|
},
|
|
format: 'YYYY-MM-DD HH:mm',
|
|
views: ['date', 'year', 'month', 'hours', 'minutes'],
|
|
autoClose: false,
|
|
position: 'relative'
|
|
});
|
|
|
|
Module.directive('dateTimeAppend', function () {
|
|
return {
|
|
link: function (scope, element) {
|
|
element.bind('click', function () {
|
|
element.find('input')[0].focus();
|
|
});
|
|
}
|
|
};
|
|
});
|
|
|
|
Module.directive('dateTime', ['$compile', '$document', '$filter', 'dateTimeConfig', '$parse', 'datePickerUtils', function ($compile, $document, $filter, dateTimeConfig, $parse, datePickerUtils) {
|
|
var body = $document.find('body');
|
|
var dateFilter = $filter('mFormat');
|
|
|
|
return {
|
|
require: 'ngModel',
|
|
scope: true,
|
|
link: function (scope, element, attrs, ngModel) {
|
|
var format = attrs.format || dateTimeConfig.format,
|
|
parentForm = element.inheritedData('$formController'),
|
|
views = $parse(attrs.views)(scope) || dateTimeConfig.views.concat(),
|
|
view = attrs.view || views[0],
|
|
index = views.indexOf(view),
|
|
dismiss = attrs.autoClose ? $parse(attrs.autoClose)(scope) : dateTimeConfig.autoClose,
|
|
picker = null,
|
|
pickerID = element[0].id,
|
|
position = attrs.position || dateTimeConfig.position,
|
|
container = null,
|
|
minDate = null,
|
|
minValid = null,
|
|
maxDate = null,
|
|
maxValid = null,
|
|
timezone = attrs.timezone || false,
|
|
eventIsForPicker = datePickerUtils.eventIsForPicker,
|
|
dateChange = null,
|
|
shownOnce = false,
|
|
template;
|
|
|
|
if (index === -1) {
|
|
views.splice(index, 1);
|
|
}
|
|
|
|
views.unshift(view);
|
|
|
|
function formatter(value) {
|
|
return dateFilter(value, format, timezone);
|
|
}
|
|
|
|
function parser(viewValue) {
|
|
if (viewValue.length === format.length) {
|
|
return viewValue;
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
function setMin(date) {
|
|
minDate = date;
|
|
attrs.minDate = date ? date.format() : date;
|
|
minValid = moment.isMoment(date);
|
|
}
|
|
|
|
function setMax(date) {
|
|
maxDate = date;
|
|
attrs.maxDate = date ? date.format() : date;
|
|
maxValid = moment.isMoment(date);
|
|
}
|
|
|
|
ngModel.$formatters.push(formatter);
|
|
ngModel.$parsers.unshift(parser);
|
|
|
|
if (angular.isDefined(attrs.minDate)) {
|
|
setMin(datePickerUtils.findParam(scope, attrs.minDate));
|
|
|
|
ngModel.$validators.min = function (value) {
|
|
//If we don't have a min / max value, then any value is valid.
|
|
return minValid ? moment.isMoment(value) && (minDate.isSame(value) || minDate.isBefore(value)) : true;
|
|
};
|
|
}
|
|
|
|
if (angular.isDefined(attrs.maxDate)) {
|
|
setMax(datePickerUtils.findParam(scope, attrs.maxDate));
|
|
|
|
ngModel.$validators.max = function (value) {
|
|
return maxValid ? moment.isMoment(value) && (maxDate.isSame(value) || maxDate.isAfter(value)) : true;
|
|
};
|
|
}
|
|
|
|
if (angular.isDefined(attrs.dateChange)) {
|
|
dateChange = datePickerUtils.findFunction(scope, attrs.dateChange);
|
|
}
|
|
|
|
function getTemplate() {
|
|
template = dateTimeConfig.template(attrs);
|
|
}
|
|
|
|
|
|
function updateInput(event) {
|
|
event.stopPropagation();
|
|
if (ngModel.$pristine) {
|
|
ngModel.$dirty = true;
|
|
ngModel.$pristine = false;
|
|
element.removeClass(PRISTINE_CLASS).addClass(DIRTY_CLASS);
|
|
if (parentForm) {
|
|
parentForm.$setDirty();
|
|
}
|
|
ngModel.$render();
|
|
}
|
|
}
|
|
|
|
function clear() {
|
|
if (picker) {
|
|
picker.remove();
|
|
picker = null;
|
|
}
|
|
if (container) {
|
|
container.remove();
|
|
container = null;
|
|
}
|
|
}
|
|
|
|
if (pickerID) {
|
|
scope.$on('pickerUpdate', function (event, pickerIDs, data) {
|
|
if (eventIsForPicker(pickerIDs, pickerID)) {
|
|
if (picker) {
|
|
//Need to handle situation where the data changed but the picker is currently open.
|
|
//To handle this, we can create the inner picker with a random ID, then forward
|
|
//any events received to it.
|
|
} else {
|
|
var validateRequired = false;
|
|
if (angular.isDefined(data.minDate)) {
|
|
setMin(data.minDate);
|
|
validateRequired = true;
|
|
}
|
|
if (angular.isDefined(data.maxDate)) {
|
|
setMax(data.maxDate);
|
|
validateRequired = true;
|
|
}
|
|
|
|
if (angular.isDefined(data.minView)) {
|
|
attrs.minView = data.minView;
|
|
}
|
|
if (angular.isDefined(data.maxView)) {
|
|
attrs.maxView = data.maxView;
|
|
}
|
|
attrs.view = data.view || attrs.view;
|
|
|
|
if (validateRequired) {
|
|
ngModel.$validate();
|
|
}
|
|
if (angular.isDefined(data.format)) {
|
|
format = attrs.format = data.format || dateTimeConfig.format;
|
|
ngModel.$modelValue = -1; //Triggers formatters. This value will be discarded.
|
|
}
|
|
getTemplate();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function showPicker() {
|
|
if (picker) {
|
|
return;
|
|
}
|
|
// create picker element
|
|
picker = $compile(template)(scope);
|
|
scope.$digest();
|
|
|
|
//If the picker has already been shown before then we shouldn't be binding to events, as these events are already bound to in this scope.
|
|
if (!shownOnce) {
|
|
scope.$on('setDate', function (event, date, view) {
|
|
updateInput(event);
|
|
if (dateChange) {
|
|
dateChange(attrs.ngModel, date);
|
|
}
|
|
if (dismiss && views[views.length - 1] === view) {
|
|
clear();
|
|
}
|
|
});
|
|
|
|
scope.$on('hidePicker', function () {
|
|
element.triggerHandler('blur');
|
|
});
|
|
|
|
scope.$on('$destroy', clear);
|
|
|
|
shownOnce = true;
|
|
}
|
|
|
|
|
|
// move picker below input element
|
|
|
|
if (position === 'absolute') {
|
|
var pos = element[0].getBoundingClientRect();
|
|
// Support IE8
|
|
var height = pos.height || element[0].offsetHeight;
|
|
picker.css({top: (pos.top + height) + 'px', left: pos.left + 'px', display: 'block', position: position});
|
|
body.append(picker);
|
|
} else {
|
|
// relative
|
|
container = angular.element('<div date-picker-wrapper></div>');
|
|
element[0].parentElement.insertBefore(container[0], element[0]);
|
|
container.append(picker);
|
|
// this approach doesn't work
|
|
// element.before(picker);
|
|
picker.css({top: element[0].offsetHeight + 'px', display: 'block'});
|
|
}
|
|
picker.bind('mousedown', function (evt) {
|
|
evt.preventDefault();
|
|
});
|
|
}
|
|
|
|
element.bind('focus', showPicker);
|
|
element.bind('blur', clear);
|
|
getTemplate();
|
|
}
|
|
};
|
|
}]);
|
|
//}));
|
|
|
|
angular.module('datePicker').run(['$templateCache', function($templateCache) {
|
|
$templateCache.put('templates/datepicker.html',
|
|
"<div ng-switch=\"view\">\r" +
|
|
"\n" +
|
|
" <div ng-switch-when=\"date\">\r" +
|
|
"\n" +
|
|
" <table>\r" +
|
|
"\n" +
|
|
" <thead>\r" +
|
|
"\n" +
|
|
" <tr>\r" +
|
|
"\n" +
|
|
" <th ng-click=\"prev()\">‹</th>\r" +
|
|
"\n" +
|
|
" <th colspan=\"5\" class=\"switch\" ng-click=\"setView('month')\" ng-bind=\"date|mFormat:'YYYY MMMM':tz\"></th>\r" +
|
|
"\n" +
|
|
" <th ng-click=\"next()\">›</i></th>\r" +
|
|
"\n" +
|
|
" </tr>\r" +
|
|
"\n" +
|
|
" <tr>\r" +
|
|
"\n" +
|
|
" <th ng-repeat=\"day in weekdays\" style=\"overflow: hidden\" ng-bind=\"day|mFormat:'ddd':tz\"></th>\r" +
|
|
"\n" +
|
|
" </tr>\r" +
|
|
"\n" +
|
|
" </thead>\r" +
|
|
"\n" +
|
|
" <tbody>\r" +
|
|
"\n" +
|
|
" <tr ng-repeat=\"week in weeks\" ng-init=\"$index2 = $index\">\r" +
|
|
"\n" +
|
|
" <td ng-repeat=\"day in week\">\r" +
|
|
"\n" +
|
|
" <span\r" +
|
|
"\n" +
|
|
" ng-class=\"classes[$index2][$index]\"\r" +
|
|
"\n" +
|
|
" ng-click=\"selectDate(day)\" ng-bind=\"day|mFormat:'DD':tz\"></span>\r" +
|
|
"\n" +
|
|
" </td>\r" +
|
|
"\n" +
|
|
" </tr>\r" +
|
|
"\n" +
|
|
" </tbody>\r" +
|
|
"\n" +
|
|
" </table>\r" +
|
|
"\n" +
|
|
" </div>\r" +
|
|
"\n" +
|
|
" <div ng-switch-when=\"year\">\r" +
|
|
"\n" +
|
|
" <table>\r" +
|
|
"\n" +
|
|
" <thead>\r" +
|
|
"\n" +
|
|
" <tr>\r" +
|
|
"\n" +
|
|
" <th ng-click=\"prev(10)\">‹</th>\r" +
|
|
"\n" +
|
|
" <th colspan=\"5\" class=\"switch\"ng-bind=\"years[0].year()+' - '+years[years.length-1].year()\"></th>\r" +
|
|
"\n" +
|
|
" <th ng-click=\"next(10)\">›</i></th>\r" +
|
|
"\n" +
|
|
" </tr>\r" +
|
|
"\n" +
|
|
" </thead>\r" +
|
|
"\n" +
|
|
" <tbody>\r" +
|
|
"\n" +
|
|
" <tr>\r" +
|
|
"\n" +
|
|
" <td colspan=\"7\">\r" +
|
|
"\n" +
|
|
" <span ng-class=\"classes[$index]\"\r" +
|
|
"\n" +
|
|
" ng-repeat=\"year in years\"\r" +
|
|
"\n" +
|
|
" ng-click=\"selectDate(year)\" ng-bind=\"year.year()\"></span>\r" +
|
|
"\n" +
|
|
" </td>\r" +
|
|
"\n" +
|
|
" </tr>\r" +
|
|
"\n" +
|
|
" </tbody>\r" +
|
|
"\n" +
|
|
" </table>\r" +
|
|
"\n" +
|
|
" </div>\r" +
|
|
"\n" +
|
|
" <div ng-switch-when=\"month\">\r" +
|
|
"\n" +
|
|
" <table>\r" +
|
|
"\n" +
|
|
" <thead>\r" +
|
|
"\n" +
|
|
" <tr>\r" +
|
|
"\n" +
|
|
" <th ng-click=\"prev()\">‹</th>\r" +
|
|
"\n" +
|
|
" <th colspan=\"5\" class=\"switch\" ng-click=\"setView('year')\" ng-bind=\"date|mFormat:'YYYY':tz\"></th>\r" +
|
|
"\n" +
|
|
" <th ng-click=\"next()\">›</i></th>\r" +
|
|
"\n" +
|
|
" </tr>\r" +
|
|
"\n" +
|
|
" </thead>\r" +
|
|
"\n" +
|
|
" <tbody>\r" +
|
|
"\n" +
|
|
" <tr>\r" +
|
|
"\n" +
|
|
" <td colspan=\"7\">\r" +
|
|
"\n" +
|
|
" <span ng-repeat=\"month in months\"\r" +
|
|
"\n" +
|
|
" ng-class=\"classes[$index]\"\r" +
|
|
"\n" +
|
|
" ng-click=\"selectDate(month)\"\r" +
|
|
"\n" +
|
|
" ng-bind=\"month|mFormat:'MMM':tz\"></span>\r" +
|
|
"\n" +
|
|
" </td>\r" +
|
|
"\n" +
|
|
" </tr>\r" +
|
|
"\n" +
|
|
" </tbody>\r" +
|
|
"\n" +
|
|
" </table>\r" +
|
|
"\n" +
|
|
" </div>\r" +
|
|
"\n" +
|
|
" <div ng-switch-when=\"hours\">\r" +
|
|
"\n" +
|
|
" <table>\r" +
|
|
"\n" +
|
|
" <thead>\r" +
|
|
"\n" +
|
|
" <tr>\r" +
|
|
"\n" +
|
|
" <th ng-click=\"prev(24)\">‹</th>\r" +
|
|
"\n" +
|
|
" <th colspan=\"5\" class=\"switch\" ng-click=\"setView('date')\" ng-bind=\"date|mFormat:'DD MMMM YYYY':tz\"></th>\r" +
|
|
"\n" +
|
|
" <th ng-click=\"next(24)\">›</i></th>\r" +
|
|
"\n" +
|
|
" </tr>\r" +
|
|
"\n" +
|
|
" </thead>\r" +
|
|
"\n" +
|
|
" <tbody>\r" +
|
|
"\n" +
|
|
" <tr>\r" +
|
|
"\n" +
|
|
" <td colspan=\"7\">\r" +
|
|
"\n" +
|
|
" <span ng-repeat=\"hour in hours\"\r" +
|
|
"\n" +
|
|
" ng-class=\"classes[$index]\"\r" +
|
|
"\n" +
|
|
" ng-click=\"selectDate(hour)\" ng-bind=\"hour|mFormat:'HH:mm':tz\"></span>\r" +
|
|
"\n" +
|
|
" </td>\r" +
|
|
"\n" +
|
|
" </tr>\r" +
|
|
"\n" +
|
|
" </tbody>\r" +
|
|
"\n" +
|
|
" </table>\r" +
|
|
"\n" +
|
|
" </div>\r" +
|
|
"\n" +
|
|
" <div ng-switch-when=\"minutes\">\r" +
|
|
"\n" +
|
|
" <table>\r" +
|
|
"\n" +
|
|
" <thead>\r" +
|
|
"\n" +
|
|
" <tr>\r" +
|
|
"\n" +
|
|
" <th ng-click=\"prev()\">‹</th>\r" +
|
|
"\n" +
|
|
" <th colspan=\"5\" class=\"switch\" ng-click=\"setView('hours')\" ng-bind=\"date|mFormat:'DD MMMM YYYY':tz\"></th>\r" +
|
|
"\n" +
|
|
" <th ng-click=\"next()\">›</i></th>\r" +
|
|
"\n" +
|
|
" </tr>\r" +
|
|
"\n" +
|
|
" </thead>\r" +
|
|
"\n" +
|
|
" <tbody>\r" +
|
|
"\n" +
|
|
" <tr>\r" +
|
|
"\n" +
|
|
" <td colspan=\"7\">\r" +
|
|
"\n" +
|
|
" <span ng-repeat=\"minute in minutes\"\r" +
|
|
"\n" +
|
|
" ng-class=\"classes[$index]\"\r" +
|
|
"\n" +
|
|
" ng-click=\"selectDate(minute)\"\r" +
|
|
"\n" +
|
|
" ng-bind=\"minute|mFormat:'HH:mm':tz\"></span>\r" +
|
|
"\n" +
|
|
" </td>\r" +
|
|
"\n" +
|
|
" </tr>\r" +
|
|
"\n" +
|
|
" </tbody>\r" +
|
|
"\n" +
|
|
" </table>\r" +
|
|
"\n" +
|
|
" </div>\r" +
|
|
"\n" +
|
|
"</div>"
|
|
);
|
|
|
|
}]);
|
|
})); |