md-crud
Version:
Angular material directive to create customizable CRUD forms.
976 lines (827 loc) • 40.8 kB
JavaScript
(function() {
"use strict";
/* global moment, angular */
var module = angular.module("mdPickers", [
"ngMaterial",
"ngAnimate",
"ngAria"
]);
module.config(["$mdIconProvider", "mdpIconsRegistry", function($mdIconProvider, mdpIconsRegistry) {
angular.forEach(mdpIconsRegistry, function(icon, index) {
$mdIconProvider.icon(icon.id, icon.url);
});
}]);
module.run(["$templateCache", "mdpIconsRegistry", function($templateCache, mdpIconsRegistry) {
angular.forEach(mdpIconsRegistry, function(icon, index) {
$templateCache.put(icon.url, icon.svg);
});
}]);
module.constant("mdpIconsRegistry", [
{
id: 'mdp-chevron-left',
url: 'mdp-chevron-left.svg',
svg: '<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/><path d="M0 0h24v24H0z" fill="none"/></svg>'
},
{
id: 'mdp-chevron-right',
url: 'mdp-chevron-right.svg',
svg: '<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/><path d="M0 0h24v24H0z" fill="none"/></svg>'
},
{
id: 'mdp-access-time',
url: 'mdp-access-time.svg',
svg: '<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"/><path d="M0 0h24v24H0z" fill="none"/><path d="M12.5 7H11v6l5.25 3.15.75-1.23-4.5-2.67z"/></svg>'
},
{
id: 'mdp-event',
url: 'mdp-event.svg',
svg: '<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M17 12h-5v5h5v-5zM16 1v2H8V1H6v2H5c-1.11 0-1.99.9-1.99 2L3 19c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2h-1V1h-2zm3 18H5V8h14v11z"/><path d="M0 0h24v24H0z" fill="none"/></svg>'
}
]);
module.directive("ngMessage", ["$mdUtil", function($mdUtil) {
return {
restrict: "EA",
priority: 101,
compile: function(element) {
var inputContainer = $mdUtil.getClosest(element, "mdp-time-picker", true) ||
$mdUtil.getClosest(element, "mdp-date-picker", true);
// If we are not a child of an input container, don't do anything
if (!inputContainer) return;
// Add our animation class
element.toggleClass('md-input-message-animation', true);
return {};
}
}
}]);
/* global moment, angular */
function DatePickerCtrl($scope, $mdDialog, $mdMedia, $timeout, currentDate, options) {
var self = this;
this.date = moment(currentDate);
this.minDate = options.minDate && moment(options.minDate).isValid() ? moment(options.minDate) : null;
this.maxDate = options.maxDate && moment(options.maxDate).isValid() ? moment(options.maxDate) : null;
this.displayFormat = options.displayFormat || "ddd, MMM DD";
this.dateFilter = angular.isFunction(options.dateFilter) ? options.dateFilter : null;
this.selectingYear = false;
// validate min and max date
if (this.minDate && this.maxDate) {
if (this.maxDate.isBefore(this.minDate)) {
this.maxDate = moment(this.minDate).add(1, 'days');
}
}
if (this.date) {
// check min date
if (this.minDate && this.date.isBefore(this.minDate)) {
this.date = moment(this.minDate);
}
// check max date
if (this.maxDate && this.date.isAfter(this.maxDate)) {
this.date = moment(this.maxDate);
}
}
this.yearItems = {
currentIndex_: 0,
PAGE_SIZE: 5,
START: (self.minDate ? self.minDate.year() : 1900),
END: (self.maxDate ? self.maxDate.year() : 0),
getItemAtIndex: function(index) {
if(this.currentIndex_ < index)
this.currentIndex_ = index;
return this.START + index;
},
getLength: function() {
return Math.min(
this.currentIndex_ + Math.floor(this.PAGE_SIZE / 2),
Math.abs(this.START - this.END) + 1
);
}
};
$scope.$mdMedia = $mdMedia;
$scope.year = this.date.year();
this.selectYear = function(year) {
self.date.year(year);
$scope.year = year;
self.selectingYear = false;
self.animate();
};
this.showYear = function() {
self.yearTopIndex = (self.date.year() - self.yearItems.START) + Math.floor(self.yearItems.PAGE_SIZE / 2);
self.yearItems.currentIndex_ = (self.date.year() - self.yearItems.START) + 1;
self.selectingYear = true;
};
this.showCalendar = function() {
self.selectingYear = false;
};
this.cancel = function() {
$mdDialog.cancel();
};
this.confirm = function() {
var date = this.date;
if (this.minDate && this.date.isBefore(this.minDate)) {
date = moment(this.minDate);
}
if (this.maxDate && this.date.isAfter(this.maxDate)) {
date = moment(this.maxDate);
}
$mdDialog.hide(date.toDate());
};
this.animate = function() {
self.animating = true;
$timeout(angular.noop).then(function() {
self.animating = false;
})
};
}
module.provider("$mdpDatePicker", function() {
var LABEL_OK = "OK",
LABEL_CANCEL = "Cancel",
DISPLAY_FORMAT = "ddd, MMM DD";
this.setDisplayFormat = function(format) {
DISPLAY_FORMAT = format;
};
this.setOKButtonLabel = function(label) {
LABEL_OK = label;
};
this.setCancelButtonLabel = function(label) {
LABEL_CANCEL = label;
};
this.$get = ["$mdDialog", function($mdDialog) {
var datePicker = function(currentDate, options) {
if (!angular.isDate(currentDate)) currentDate = Date.now();
if (!angular.isObject(options)) options = {};
options.displayFormat = DISPLAY_FORMAT;
return $mdDialog.show({
controller: ['$scope', '$mdDialog', '$mdMedia', '$timeout', 'currentDate', 'options', DatePickerCtrl],
controllerAs: 'datepicker',
clickOutsideToClose: true,
template: '<md-dialog aria-label="" class="mdp-datepicker" ng-class="{ \'portrait\': !$mdMedia(\'gt-xs\') }">' +
'<md-dialog-content layout="row" layout-wrap>' +
'<div layout="column" layout-align="start center">' +
'<md-toolbar layout-align="start start" flex class="mdp-datepicker-date-wrapper md-hue-1 md-primary" layout="column">' +
'<span class="mdp-datepicker-year" ng-click="datepicker.showYear()" ng-class="{ \'active\': datepicker.selectingYear }">{{ datepicker.date.format(\'YYYY\') }}</span>' +
'<span class="mdp-datepicker-date" ng-click="datepicker.showCalendar()" ng-class="{ \'active\': !datepicker.selectingYear }">{{ datepicker.date.format(datepicker.displayFormat) }}</span> ' +
'</md-toolbar>' +
'</div>' +
'<div>' +
'<div class="mdp-datepicker-select-year mdp-animation-zoom" layout="column" layout-align="center start" ng-if="datepicker.selectingYear">' +
'<md-virtual-repeat-container md-auto-shrink md-top-index="datepicker.yearTopIndex">' +
'<div flex md-virtual-repeat="item in datepicker.yearItems" md-on-demand class="repeated-year">' +
'<span class="md-button" ng-click="datepicker.selectYear(item)" md-ink-ripple ng-class="{ \'md-primary current\': item == year }">{{ item }}</span>' +
'</div>' +
'</md-virtual-repeat-container>' +
'</div>' +
'<mdp-calendar ng-if="!datepicker.selectingYear" class="mdp-animation-zoom" date="datepicker.date" min-date="datepicker.minDate" date-filter="datepicker.dateFilter" max-date="datepicker.maxDate"></mdp-calendar>' +
'<md-dialog-actions layout="row">' +
'<span flex></span>' +
'<md-button ng-click="datepicker.cancel()" aria-label="' + LABEL_CANCEL + '">' + LABEL_CANCEL + '</md-button>' +
'<md-button ng-click="datepicker.confirm()" class="md-primary" aria-label="' + LABEL_OK + '">' + LABEL_OK + '</md-button>' +
'</md-dialog-actions>' +
'</div>' +
'</md-dialog-content>' +
'</md-dialog>',
targetEvent: options.targetEvent,
locals: {
currentDate: currentDate,
options: options
},
skipHide: true
});
};
return datePicker;
}];
});
function CalendarCtrl($scope) {
var self = this;
this.dow = moment.localeData().firstDayOfWeek();
this.weekDays = [].concat(
moment.weekdaysMin().slice(
this.dow
),
moment.weekdaysMin().slice(
0,
this.dow
)
);
this.daysInMonth = [];
this.getDaysInMonth = function() {
var days = self.date.daysInMonth(),
firstDay = moment(self.date).date(1).day() - this.dow;
if(firstDay < 0) firstDay = this.weekDays.length - 1;
var arr = [];
for(var i = 1; i <= (firstDay + days); i++) {
var day = null;
if(i > firstDay) {
day = {
value: (i - firstDay),
enabled: self.isDayEnabled(moment(self.date).date(i - firstDay).toDate())
};
}
arr.push(day);
}
return arr;
};
this.isDayEnabled = function(day) {
return (!this.minDate || this.minDate <= day) &&
(!this.maxDate || this.maxDate >= day) &&
(!self.dateFilter || !self.dateFilter(day));
};
this.selectDate = function(dom) {
self.date.date(dom);
};
this.nextMonth = function() {
self.date.add(1, 'months');
};
this.prevMonth = function() {
self.date.subtract(1, 'months');
};
this.updateDaysInMonth = function() {
self.daysInMonth = self.getDaysInMonth();
};
$scope.$watch(function() { return self.date.unix() }, function(newValue, oldValue) {
if(newValue && newValue !== oldValue)
self.updateDaysInMonth();
})
self.updateDaysInMonth();
}
module.directive("mdpCalendar", ["$animate", function($animate) {
return {
restrict: 'E',
bindToController: {
"date": "=",
"minDate": "=",
"maxDate": "=",
"dateFilter": "="
},
template: '<div class="mdp-calendar">' +
'<div layout="row" layout-align="space-between center">' +
'<md-button aria-label="previous month" class="md-icon-button" ng-click="calendar.prevMonth()"><md-icon md-svg-icon="mdp-chevron-left"></md-icon></md-button>' +
'<div class="mdp-calendar-monthyear" ng-show="!calendar.animating">{{ calendar.date.format("MMMM YYYY") }}</div>' +
'<md-button aria-label="next month" class="md-icon-button" ng-click="calendar.nextMonth()"><md-icon md-svg-icon="mdp-chevron-right"></md-icon></md-button>' +
'</div>' +
'<div layout="row" layout-align="space-around center" class="mdp-calendar-week-days" ng-show="!calendar.animating">' +
'<div layout layout-align="center center" ng-repeat="d in calendar.weekDays track by $index">{{ d }}</div>' +
'</div>' +
'<div layout="row" layout-align="start center" layout-wrap class="mdp-calendar-days" ng-class="{ \'mdp-animate-next\': calendar.animating }" ng-show="!calendar.animating" md-swipe-left="calendar.nextMonth()" md-swipe-right="calendar.prevMonth()">' +
'<div layout layout-align="center center" ng-repeat-start="day in calendar.daysInMonth track by $index" ng-class="{ \'mdp-day-placeholder\': !day }">' +
'<md-button class="md-icon-button md-raised" aria-label="Select day" ng-if="day" ng-class="{ \'md-accent\': calendar.date.date() == day.value }" ng-click="calendar.selectDate(day.value)" ng-disabled="!day.enabled">{{ day.value }}</md-button>' +
'</div>' +
'<div flex="100" ng-if="($index + 1) % 7 == 0" ng-repeat-end></div>' +
'</div>' +
'</div>',
controller: ["$scope", CalendarCtrl],
controllerAs: "calendar",
link: function(scope, element, attrs, ctrl) {
var animElements = [
element[0].querySelector(".mdp-calendar-week-days"),
element[0].querySelector('.mdp-calendar-days'),
element[0].querySelector('.mdp-calendar-monthyear')
].map(function(a) {
return angular.element(a);
});
scope.$watch(function() { return ctrl.date.format("YYYYMM") }, function(newValue, oldValue) {
var direction = null;
if(newValue > oldValue)
direction = "mdp-animate-next";
else if(newValue < oldValue)
direction = "mdp-animate-prev";
if(direction) {
for(var i in animElements) {
animElements[i].addClass(direction);
$animate.removeClass(animElements[i], direction);
}
}
});
}
}
}]);
function formatValidator(value, format) {
return !value || angular.isDate(value) || moment(value, format, true).isValid();
}
function minDateValidator(value, format, minDate) {
var minDate = moment(minDate, "YYYY-MM-DD", true);
var date = angular.isDate(value) ? moment(value) : moment(value, format, true);
return !value ||
angular.isDate(value) ||
!minDate.isValid() ||
date.isSameOrAfter(minDate);
}
function maxDateValidator(value, format, maxDate) {
var maxDate = moment(maxDate, "YYYY-MM-DD", true);
var date = angular.isDate(value) ? moment(value) : moment(value, format, true);
return !value ||
angular.isDate(value) ||
!maxDate.isValid() ||
date.isSameOrBefore(maxDate);
}
function filterValidator(value, format, filter) {
var date = angular.isDate(value) ? moment(value) : moment(value, format, true);
return !value ||
angular.isDate(value) ||
!angular.isFunction(filter) ||
!filter(date);
}
function requiredValidator(value, ngModel) {
return value
}
module.directive("mdpDatePicker", ["$mdpDatePicker", "$timeout", function($mdpDatePicker, $timeout) {
return {
restrict: 'E',
require: 'ngModel',
transclude: true,
template: function(element, attrs) {
var noFloat = angular.isDefined(attrs.mdpNoFloat),
placeholder = angular.isDefined(attrs.mdpPlaceholder) ? attrs.mdpPlaceholder : "",
openOnClick = angular.isDefined(attrs.mdpOpenOnClick) ? true : false;
return '<div layout layout-align="start start">' +
'<md-button' + (angular.isDefined(attrs.mdpDisabled) ? ' ng-disabled="disabled"' : '') + ' class="md-icon-button" ng-click="showPicker($event)">' +
'<md-icon md-svg-icon="mdp-event"></md-icon>' +
'</md-button>' +
'<md-input-container' + (noFloat ? ' md-no-float' : '') + ' md-is-error="isError()">' +
'<input type="{{ ::type }}"' + (angular.isDefined(attrs.mdpDisabled) ? ' ng-disabled="disabled"' : '') + ' aria-label="' + placeholder + '" placeholder="' + placeholder + '"' + (openOnClick ? ' ng-click="showPicker($event)" ' : '') + ' />' +
'</md-input-container>' +
'</div>';
},
scope: {
"minDate": "=mdpMinDate",
"maxDate": "=mdpMaxDate",
"dateFilter": "=mdpDateFilter",
"dateFormat": "@mdpFormat",
"placeholder": "@mdpPlaceholder",
"noFloat": "=mdpNoFloat",
"openOnClick": "=mdpOpenOnClick",
"disabled": "=?mdpDisabled"
},
link: {
pre: function(scope, element, attrs, ngModel, $transclude) {
},
post: function(scope, element, attrs, ngModel, $transclude) {
var inputElement = angular.element(element[0].querySelector('input')),
inputContainer = angular.element(element[0].querySelector('md-input-container')),
inputContainerCtrl = inputContainer.controller("mdInputContainer");
$transclude(function(clone) {
inputContainer.append(clone);
});
var messages = angular.element(inputContainer[0].querySelector("[ng-messages]"));
scope.type = scope.dateFormat ? "text" : "date"
scope.dateFormat = scope.dateFormat || "YYYY-MM-DD";
scope.model = ngModel;
scope.isError = function() {
return !ngModel.$pristine && !!ngModel.$invalid;
};
// update input element if model has changed
ngModel.$formatters.unshift(function(value) {
var date = angular.isDate(value) && moment(value);
if(date && date.isValid())
updateInputElement(date.format(scope.dateFormat));
else
updateInputElement(null);
});
ngModel.$validators.format = function(modelValue, viewValue) {
return formatValidator(viewValue, scope.dateFormat);
};
ngModel.$validators.minDate = function(modelValue, viewValue) {
return minDateValidator(viewValue, scope.dateFormat, scope.minDate);
};
ngModel.$validators.maxDate = function(modelValue, viewValue) {
return maxDateValidator(viewValue, scope.dateFormat, scope.maxDate);
};
ngModel.$validators.filter = function(modelValue, viewValue) {
return filterValidator(viewValue, scope.dateFormat, scope.dateFilter);
};
ngModel.$validators.required = function(modelValue, viewValue) {
return angular.isUndefined(attrs.required) || !ngModel.$isEmpty(modelValue) || !ngModel.$isEmpty(viewValue);
};
ngModel.$parsers.unshift(function(value) {
var parsed = moment(value, scope.dateFormat, true);
if(parsed.isValid()) {
if(angular.isDate(ngModel.$modelValue)) {
var originalModel = moment(ngModel.$modelValue);
originalModel.year(parsed.year());
originalModel.month(parsed.month());
originalModel.date(parsed.date());
parsed = originalModel;
}
return parsed.toDate();
} else
return null;
});
// update input element value
function updateInputElement(value) {
inputElement[0].value = value;
inputContainerCtrl.setHasValue(!ngModel.$isEmpty(value));
}
function updateDate(date) {
var value = moment(date, angular.isDate(date) ? null : scope.dateFormat, true),
strValue = value.format(scope.dateFormat);
if(value.isValid()) {
updateInputElement(strValue);
ngModel.$setViewValue(strValue);
} else {
updateInputElement(date);
ngModel.$setViewValue(date);
}
if(!ngModel.$pristine &&
messages.hasClass("md-auto-hide") &&
inputContainer.hasClass("md-input-invalid")) messages.removeClass("md-auto-hide");
ngModel.$render();
}
scope.showPicker = function(ev) {
$mdpDatePicker(ngModel.$modelValue, {
minDate: scope.minDate,
maxDate: scope.maxDate,
dateFilter: scope.dateFilter,
targetEvent: ev
}).then(updateDate);
};
function onInputElementEvents(event) {
if(event.target.value !== ngModel.$viewVaue)
updateDate(event.target.value);
}
inputElement.on("reset input blur", onInputElementEvents);
scope.$on("$destroy", function() {
inputElement.off("reset input blur", onInputElementEvents);
});
}
}
};
}]);
module.directive("mdpDatePicker", ["$mdpDatePicker", "$timeout", function($mdpDatePicker, $timeout) {
return {
restrict: 'A',
require: 'ngModel',
scope: {
"minDate": "@min",
"maxDate": "@max",
"dateFilter": "=mdpDateFilter",
"dateFormat": "@mdpFormat",
},
link: function(scope, element, attrs, ngModel, $transclude) {
scope.dateFormat = scope.dateFormat || "YYYY-MM-DD";
ngModel.$validators.format = function(modelValue, viewValue) {
return formatValidator(viewValue, scope.format);
};
ngModel.$validators.minDate = function(modelValue, viewValue) {
return minDateValidator(viewValue, scope.format, scope.minDate);
};
ngModel.$validators.maxDate = function(modelValue, viewValue) {
return maxDateValidator(viewValue, scope.format, scope.maxDate);
};
ngModel.$validators.filter = function(modelValue, viewValue) {
return filterValidator(viewValue, scope.format, scope.dateFilter);
};
function showPicker(ev) {
$mdpDatePicker(ngModel.$modelValue, {
minDate: scope.minDate,
maxDate: scope.maxDate,
dateFilter: scope.dateFilter,
targetEvent: ev
}).then(function(time) {
ngModel.$setViewValue(moment(time).format(scope.format));
ngModel.$render();
});
};
element.on("click", showPicker);
scope.$on("$destroy", function() {
element.off("click", showPicker);
});
}
}
}]);
/* global moment, angular */
function TimePickerCtrl($scope, $mdDialog, time, autoSwitch, $mdMedia) {
var self = this;
this.VIEW_HOURS = 1;
this.VIEW_MINUTES = 2;
this.currentView = this.VIEW_HOURS;
this.time = moment(time);
this.autoSwitch = !!autoSwitch;
this.clockHours = parseInt(this.time.format("h"));
this.clockMinutes = parseInt(this.time.minutes());
$scope.$mdMedia = $mdMedia;
this.switchView = function() {
self.currentView = self.currentView == self.VIEW_HOURS ? self.VIEW_MINUTES : self.VIEW_HOURS;
};
this.setAM = function() {
if(self.time.hours() >= 12)
self.time.hour(self.time.hour() - 12);
};
this.setPM = function() {
if(self.time.hours() < 12)
self.time.hour(self.time.hour() + 12);
};
this.cancel = function() {
$mdDialog.cancel();
};
this.confirm = function() {
$mdDialog.hide(this.time.toDate());
};
}
function ClockCtrl($scope) {
var TYPE_HOURS = "hours";
var TYPE_MINUTES = "minutes";
var self = this;
this.STEP_DEG = 360 / 12;
this.steps = [];
this.CLOCK_TYPES = {
"hours": {
range: 12,
},
"minutes": {
range: 60,
}
}
this.getPointerStyle = function() {
var divider = 1;
switch(self.type) {
case TYPE_HOURS:
divider = 12;
break;
case TYPE_MINUTES:
divider = 60;
break;
}
var degrees = Math.round(self.selected * (360 / divider)) - 180;
return {
"-webkit-transform": "rotate(" + degrees + "deg)",
"-ms-transform": "rotate(" + degrees + "deg)",
"transform": "rotate(" + degrees + "deg)"
}
};
this.setTimeByDeg = function(deg) {
deg = deg >= 360 ? 0 : deg;
var divider = 0;
switch(self.type) {
case TYPE_HOURS:
divider = 12;
break;
case TYPE_MINUTES:
divider = 60;
break;
}
self.setTime(
Math.round(divider / 360 * deg)
);
};
this.setTime = function(time, type) {
this.selected = time;
switch(self.type) {
case TYPE_HOURS:
if(self.time.format("A") == "PM") time += 12;
this.time.hours(time);
break;
case TYPE_MINUTES:
if(time > 59) time -= 60;
this.time.minutes(time);
break;
}
};
this.init = function() {
self.type = self.type || "hours";
switch(self.type) {
case TYPE_HOURS:
for(var i = 1; i <= 12; i++)
self.steps.push(i);
self.selected = self.time.hours() || 0;
if(self.selected > 12) self.selected -= 12;
break;
case TYPE_MINUTES:
for(var i = 5; i <= 55; i+=5)
self.steps.push(i);
self.steps.push(0);
self.selected = self.time.minutes() || 0;
break;
}
};
this.init();
}
module.directive("mdpClock", ["$animate", "$timeout", function($animate, $timeout) {
return {
restrict: 'E',
bindToController: {
'type': '@?',
'time': '=',
'autoSwitch': '=?'
},
replace: true,
template: '<div class="mdp-clock">' +
'<div class="mdp-clock-container">' +
'<md-toolbar class="mdp-clock-center md-primary"></md-toolbar>' +
'<md-toolbar ng-style="clock.getPointerStyle()" class="mdp-pointer md-primary">' +
'<span class="mdp-clock-selected md-button md-raised md-primary"></span>' +
'</md-toolbar>' +
'<md-button ng-class="{ \'md-primary\': clock.selected == step }" class="md-icon-button md-raised mdp-clock-deg{{ ::(clock.STEP_DEG * ($index + 1)) }}" ng-repeat="step in clock.steps" ng-click="clock.setTime(step)">{{ step }}</md-button>' +
'</div>' +
'</div>',
controller: ["$scope", ClockCtrl],
controllerAs: "clock",
link: function(scope, element, attrs, ctrl) {
var pointer = angular.element(element[0].querySelector(".mdp-pointer")),
timepickerCtrl = scope.$parent.timepicker;
var onEvent = function(event) {
var containerCoords = event.currentTarget.getClientRects()[0];
var x = ((event.currentTarget.offsetWidth / 2) - (event.pageX - containerCoords.left)),
y = ((event.pageY - containerCoords.top) - (event.currentTarget.offsetHeight / 2));
var deg = Math.round((Math.atan2(x, y) * (180 / Math.PI)));
$timeout(function() {
ctrl.setTimeByDeg(deg + 180);
if(ctrl.autoSwitch && ["mouseup", "click"].indexOf(event.type) !== -1 && timepickerCtrl) timepickerCtrl.switchView();
});
};
element.on("mousedown", function() {
element.on("mousemove", onEvent);
});
element.on("mouseup", function(e) {
element.off("mousemove");
});
element.on("click", onEvent);
scope.$on("$destroy", function() {
element.off("click", onEvent);
element.off("mousemove", onEvent);
});
}
}
}]);
module.provider("$mdpTimePicker", function() {
var LABEL_OK = "OK",
LABEL_CANCEL = "Cancel";
this.setOKButtonLabel = function(label) {
LABEL_OK = label;
};
this.setCancelButtonLabel = function(label) {
LABEL_CANCEL = label;
};
this.$get = ["$mdDialog", function($mdDialog) {
var timePicker = function(time, options) {
if(!angular.isDate(time)) time = Date.now();
if (!angular.isObject(options)) options = {};
return $mdDialog.show({
controller: ['$scope', '$mdDialog', 'time', 'autoSwitch', '$mdMedia', TimePickerCtrl],
controllerAs: 'timepicker',
clickOutsideToClose: true,
template: '<md-dialog aria-label="" class="mdp-timepicker" ng-class="{ \'portrait\': !$mdMedia(\'gt-xs\') }">' +
'<md-dialog-content layout-gt-xs="row" layout-wrap>' +
'<md-toolbar layout-gt-xs="column" layout-xs="row" layout-align="center center" flex class="mdp-timepicker-time md-hue-1 md-primary">' +
'<div class="mdp-timepicker-selected-time">' +
'<span ng-class="{ \'active\': timepicker.currentView == timepicker.VIEW_HOURS }" ng-click="timepicker.currentView = timepicker.VIEW_HOURS">{{ timepicker.time.format("h") }}</span>:' +
'<span ng-class="{ \'active\': timepicker.currentView == timepicker.VIEW_MINUTES }" ng-click="timepicker.currentView = timepicker.VIEW_MINUTES">{{ timepicker.time.format("mm") }}</span>' +
'</div>' +
'<div layout="column" class="mdp-timepicker-selected-ampm">' +
'<span ng-click="timepicker.setAM()" ng-class="{ \'active\': timepicker.time.hours() < 12 }">AM</span>' +
'<span ng-click="timepicker.setPM()" ng-class="{ \'active\': timepicker.time.hours() >= 12 }">PM</span>' +
'</div>' +
'</md-toolbar>' +
'<div>' +
'<div class="mdp-clock-switch-container" ng-switch="timepicker.currentView" layout layout-align="center center">' +
'<mdp-clock class="mdp-animation-zoom" auto-switch="timepicker.autoSwitch" time="timepicker.time" type="hours" ng-switch-when="1"></mdp-clock>' +
'<mdp-clock class="mdp-animation-zoom" auto-switch="timepicker.autoSwitch" time="timepicker.time" type="minutes" ng-switch-when="2"></mdp-clock>' +
'</div>' +
'<md-dialog-actions layout="row">' +
'<span flex></span>' +
'<md-button ng-click="timepicker.cancel()" aria-label="' + LABEL_CANCEL + '">' + LABEL_CANCEL + '</md-button>' +
'<md-button ng-click="timepicker.confirm()" class="md-primary" aria-label="' + LABEL_OK + '">' + LABEL_OK + '</md-button>' +
'</md-dialog-actions>' +
'</div>' +
'</md-dialog-content>' +
'</md-dialog>',
targetEvent: options.targetEvent,
locals: {
time: time,
autoSwitch: options.autoSwitch
},
skipHide: true
});
};
return timePicker;
}];
});
module.directive("mdpTimePicker", ["$mdpTimePicker", "$timeout", function($mdpTimePicker, $timeout) {
return {
restrict: 'E',
require: 'ngModel',
transclude: true,
template: function(element, attrs) {
var noFloat = angular.isDefined(attrs.mdpNoFloat),
placeholder = angular.isDefined(attrs.mdpPlaceholder) ? attrs.mdpPlaceholder : "",
openOnClick = angular.isDefined(attrs.mdpOpenOnClick) ? true : false;
return '<div layout layout-align="start start">' +
'<md-button class="md-icon-button" ng-click="showPicker($event)"' + (angular.isDefined(attrs.mdpDisabled) ? ' ng-disabled="disabled"' : '') + '>' +
'<md-icon md-svg-icon="mdp-access-time"></md-icon>' +
'</md-button>' +
'<md-input-container' + (noFloat ? ' md-no-float' : '') + ' md-is-error="isError()">' +
'<input type="{{ ::type }}"' + (angular.isDefined(attrs.mdpDisabled) ? ' ng-disabled="disabled"' : '') + ' aria-label="' + placeholder + '" placeholder="' + placeholder + '"' + (openOnClick ? ' ng-click="showPicker($event)" ' : '') + ' />' +
'</md-input-container>' +
'</div>';
},
scope: {
"timeFormat": "@mdpFormat",
"placeholder": "@mdpPlaceholder",
"autoSwitch": "=?mdpAutoSwitch",
"disabled": "=?mdpDisabled"
},
link: function(scope, element, attrs, ngModel, $transclude) {
var inputElement = angular.element(element[0].querySelector('input')),
inputContainer = angular.element(element[0].querySelector('md-input-container')),
inputContainerCtrl = inputContainer.controller("mdInputContainer");
$transclude(function(clone) {
inputContainer.append(clone);
});
var messages = angular.element(inputContainer[0].querySelector("[ng-messages]"));
scope.type = scope.timeFormat ? "text" : "time"
scope.timeFormat = scope.timeFormat || "HH:mm";
scope.autoSwitch = scope.autoSwitch || false;
scope.$watch(function() { return ngModel.$error }, function(newValue, oldValue) {
inputContainerCtrl.setInvalid(!ngModel.$pristine && !!Object.keys(ngModel.$error).length);
}, true);
// update input element if model has changed
ngModel.$formatters.unshift(function(value) {
var time = angular.isDate(value) && moment(value);
if(time && time.isValid())
updateInputElement(time.format(scope.timeFormat));
else
updateInputElement(null);
});
ngModel.$validators.format = function(modelValue, viewValue) {
return !viewValue || angular.isDate(viewValue) || moment(viewValue, scope.timeFormat, true).isValid();
};
ngModel.$validators.required = function(modelValue, viewValue) {
return angular.isUndefined(attrs.required) || !ngModel.$isEmpty(modelValue) || !ngModel.$isEmpty(viewValue);
};
ngModel.$parsers.unshift(function(value) {
var parsed = moment(value, scope.timeFormat, true);
if(parsed.isValid()) {
if(angular.isDate(ngModel.$modelValue)) {
var originalModel = moment(ngModel.$modelValue);
originalModel.minutes(parsed.minutes());
originalModel.hours(parsed.hours());
originalModel.seconds(parsed.seconds());
parsed = originalModel;
}
return parsed.toDate();
} else
return null;
});
// update input element value
function updateInputElement(value) {
inputElement[0].value = value;
inputContainerCtrl.setHasValue(!ngModel.$isEmpty(value));
}
function updateTime(time) {
var value = moment(time, angular.isDate(time) ? null : scope.timeFormat, true),
strValue = value.format(scope.timeFormat);
if(value.isValid()) {
updateInputElement(strValue);
ngModel.$setViewValue(strValue);
} else {
updateInputElement(time);
ngModel.$setViewValue(time);
}
if(!ngModel.$pristine &&
messages.hasClass("md-auto-hide") &&
inputContainer.hasClass("md-input-invalid")) messages.removeClass("md-auto-hide");
ngModel.$render();
}
scope.showPicker = function(ev) {
$mdpTimePicker(ngModel.$modelValue, {
targetEvent: ev,
autoSwitch: scope.autoSwitch
}).then(function(time) {
updateTime(time, true);
});
};
function onInputElementEvents(event) {
if(event.target.value !== ngModel.$viewVaue)
updateTime(event.target.value);
}
inputElement.on("reset input blur", onInputElementEvents);
scope.$on("$destroy", function() {
inputElement.off("reset input blur", onInputElementEvents);
})
}
};
}]);
module.directive("mdpTimePicker", ["$mdpTimePicker", "$timeout", function($mdpTimePicker, $timeout) {
return {
restrict: 'A',
require: 'ngModel',
scope: {
"timeFormat": "@mdpFormat",
"autoSwitch": "=?mdpAutoSwitch",
},
link: function(scope, element, attrs, ngModel, $transclude) {
scope.format = scope.format || "HH:mm";
function showPicker(ev) {
$mdpTimePicker(ngModel.$modelValue, {
targetEvent: ev,
autoSwitch: scope.autoSwitch
}).then(function(time) {
ngModel.$setViewValue(moment(time).format(scope.format));
ngModel.$render();
});
};
element.on("click", showPicker);
scope.$on("$destroy", function() {
element.off("click", showPicker);
});
}
}
}]);
})();