angular-strap-rails 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.DS_Store +0 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +45 -0
- data/Rakefile +1 -0
- data/angular-strap-rails.gemspec +15 -0
- data/lib/angular-strap-rails.rb +8 -0
- data/lib/angular-strap-rails/version.rb +5 -0
- data/vendor/.DS_Store +0 -0
- data/vendor/assets/.DS_Store +0 -0
- data/vendor/assets/javascripts/.DS_Store +0 -0
- data/vendor/assets/javascripts/angular-strap.coffee +0 -0
- data/vendor/assets/javascripts/angular-strap/.DS_Store +0 -0
- data/vendor/assets/javascripts/angular-strap/datepicker.coffee +11 -0
- data/vendor/assets/javascripts/angular-strap/modal.coffee +9 -0
- data/vendor/assets/javascripts/dist/angular-strap.js +3682 -0
- data/vendor/assets/javascripts/dist/angular-strap.tpl.js +100 -0
- data/vendor/assets/javascripts/dist/modules/affix.js +191 -0
- data/vendor/assets/javascripts/dist/modules/alert.js +114 -0
- data/vendor/assets/javascripts/dist/modules/alert.tpl.js +14 -0
- data/vendor/assets/javascripts/dist/modules/aside.js +96 -0
- data/vendor/assets/javascripts/dist/modules/aside.tpl.js +14 -0
- data/vendor/assets/javascripts/dist/modules/button.js +141 -0
- data/vendor/assets/javascripts/dist/modules/date-parser.js +150 -0
- data/vendor/assets/javascripts/dist/modules/datepicker.js +583 -0
- data/vendor/assets/javascripts/dist/modules/datepicker.tpl.js +14 -0
- data/vendor/assets/javascripts/dist/modules/debounce.js +60 -0
- data/vendor/assets/javascripts/dist/modules/dimensions.js +142 -0
- data/vendor/assets/javascripts/dist/modules/dropdown.js +124 -0
- data/vendor/assets/javascripts/dist/modules/dropdown.tpl.js +14 -0
- data/vendor/assets/javascripts/dist/modules/modal.js +282 -0
- data/vendor/assets/javascripts/dist/modules/modal.tpl.js +14 -0
- data/vendor/assets/javascripts/dist/modules/navbar.js +55 -0
- data/vendor/assets/javascripts/dist/modules/parse-options.js +51 -0
- data/vendor/assets/javascripts/dist/modules/popover.js +100 -0
- data/vendor/assets/javascripts/dist/modules/popover.tpl.js +14 -0
- data/vendor/assets/javascripts/dist/modules/raf.js +45 -0
- data/vendor/assets/javascripts/dist/modules/scrollspy.js +229 -0
- data/vendor/assets/javascripts/dist/modules/select.js +281 -0
- data/vendor/assets/javascripts/dist/modules/select.tpl.js +14 -0
- data/vendor/assets/javascripts/dist/modules/tab.js +69 -0
- data/vendor/assets/javascripts/dist/modules/tab.tpl.js +14 -0
- data/vendor/assets/javascripts/dist/modules/timepicker.js +430 -0
- data/vendor/assets/javascripts/dist/modules/timepicker.tpl.js +14 -0
- data/vendor/assets/javascripts/dist/modules/tooltip.js +405 -0
- data/vendor/assets/javascripts/dist/modules/tooltip.tpl.js +14 -0
- data/vendor/assets/javascripts/dist/modules/typeahead.js +225 -0
- data/vendor/assets/javascripts/dist/modules/typeahead.tpl.js +14 -0
- data/vendor/assets/stylesheets/angular-strap.css +564 -0
- metadata +94 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* angular-strap
|
|
3
|
+
* @version v2.0.1 - 2014-04-10
|
|
4
|
+
* @link http://mgcrea.github.io/angular-strap
|
|
5
|
+
* @author Olivier Louvignes (olivier@mg-crea.com)
|
|
6
|
+
* @license MIT License, http://www.opensource.org/licenses/MIT
|
|
7
|
+
*/
|
|
8
|
+
'use strict';
|
|
9
|
+
angular.module('mgcrea.ngStrap.aside').run([
|
|
10
|
+
'$templateCache',
|
|
11
|
+
function ($templateCache) {
|
|
12
|
+
$templateCache.put('aside/aside.tpl.html', '<div class="aside" tabindex="-1" role="dialog"><div class="aside-dialog"><div class="aside-content"><div class="aside-header" ng-show="title"><button type="button" class="close" ng-click="$hide()">×</button><h4 class="aside-title" ng-bind="title"></h4></div><div class="aside-body" ng-bind="content"></div><div class="aside-footer"><button type="button" class="btn btn-default" ng-click="$hide()">Close</button></div></div></div></div>');
|
|
13
|
+
}
|
|
14
|
+
]);
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* angular-strap
|
|
3
|
+
* @version v2.0.1 - 2014-04-10
|
|
4
|
+
* @link http://mgcrea.github.io/angular-strap
|
|
5
|
+
* @author Olivier Louvignes (olivier@mg-crea.com)
|
|
6
|
+
* @license MIT License, http://www.opensource.org/licenses/MIT
|
|
7
|
+
*/
|
|
8
|
+
'use strict';
|
|
9
|
+
angular.module('mgcrea.ngStrap.button', []).provider('$button', function () {
|
|
10
|
+
var defaults = this.defaults = {
|
|
11
|
+
activeClass: 'active',
|
|
12
|
+
toggleEvent: 'click'
|
|
13
|
+
};
|
|
14
|
+
this.$get = function () {
|
|
15
|
+
return { defaults: defaults };
|
|
16
|
+
};
|
|
17
|
+
}).directive('bsCheckboxGroup', function () {
|
|
18
|
+
return {
|
|
19
|
+
restrict: 'A',
|
|
20
|
+
require: 'ngModel',
|
|
21
|
+
compile: function postLink(element, attr) {
|
|
22
|
+
element.attr('data-toggle', 'buttons');
|
|
23
|
+
element.removeAttr('ng-model');
|
|
24
|
+
var children = element[0].querySelectorAll('input[type="checkbox"]');
|
|
25
|
+
angular.forEach(children, function (child) {
|
|
26
|
+
var childEl = angular.element(child);
|
|
27
|
+
childEl.attr('bs-checkbox', '');
|
|
28
|
+
childEl.attr('ng-model', attr.ngModel + '.' + childEl.attr('value'));
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
}).directive('bsCheckbox', [
|
|
33
|
+
'$button',
|
|
34
|
+
'$$rAF',
|
|
35
|
+
function ($button, $$rAF) {
|
|
36
|
+
var defaults = $button.defaults;
|
|
37
|
+
var constantValueRegExp = /^(true|false|\d+)$/;
|
|
38
|
+
return {
|
|
39
|
+
restrict: 'A',
|
|
40
|
+
require: 'ngModel',
|
|
41
|
+
link: function postLink(scope, element, attr, controller) {
|
|
42
|
+
var options = defaults;
|
|
43
|
+
// Support label > input[type="checkbox"]
|
|
44
|
+
var isInput = element[0].nodeName === 'INPUT';
|
|
45
|
+
var activeElement = isInput ? element.parent() : element;
|
|
46
|
+
var trueValue = angular.isDefined(attr.trueValue) ? attr.trueValue : true;
|
|
47
|
+
if (constantValueRegExp.test(attr.trueValue)) {
|
|
48
|
+
trueValue = scope.$eval(attr.trueValue);
|
|
49
|
+
}
|
|
50
|
+
var falseValue = angular.isDefined(attr.falseValue) ? attr.falseValue : false;
|
|
51
|
+
if (constantValueRegExp.test(attr.falseValue)) {
|
|
52
|
+
falseValue = scope.$eval(attr.falseValue);
|
|
53
|
+
}
|
|
54
|
+
// Parse exotic values
|
|
55
|
+
var hasExoticValues = typeof trueValue !== 'boolean' || typeof falseValue !== 'boolean';
|
|
56
|
+
if (hasExoticValues) {
|
|
57
|
+
controller.$parsers.push(function (viewValue) {
|
|
58
|
+
// console.warn('$parser', element.attr('ng-model'), 'viewValue', viewValue);
|
|
59
|
+
return viewValue ? trueValue : falseValue;
|
|
60
|
+
});
|
|
61
|
+
// Fix rendering for exotic values
|
|
62
|
+
scope.$watch(attr.ngModel, function (newValue, oldValue) {
|
|
63
|
+
controller.$render();
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
// model -> view
|
|
67
|
+
controller.$render = function () {
|
|
68
|
+
// console.warn('$render', element.attr('ng-model'), 'controller.$modelValue', typeof controller.$modelValue, controller.$modelValue, 'controller.$viewValue', typeof controller.$viewValue, controller.$viewValue);
|
|
69
|
+
var isActive = angular.equals(controller.$modelValue, trueValue);
|
|
70
|
+
$$rAF(function () {
|
|
71
|
+
if (isInput)
|
|
72
|
+
element[0].checked = isActive;
|
|
73
|
+
activeElement.toggleClass(options.activeClass, isActive);
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
// view -> model
|
|
77
|
+
element.bind(options.toggleEvent, function () {
|
|
78
|
+
scope.$apply(function () {
|
|
79
|
+
// console.warn('!click', element.attr('ng-model'), 'controller.$viewValue', typeof controller.$viewValue, controller.$viewValue, 'controller.$modelValue', typeof controller.$modelValue, controller.$modelValue);
|
|
80
|
+
if (!isInput) {
|
|
81
|
+
controller.$setViewValue(!activeElement.hasClass('active'));
|
|
82
|
+
}
|
|
83
|
+
if (!hasExoticValues) {
|
|
84
|
+
controller.$render();
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
]).directive('bsRadioGroup', function () {
|
|
92
|
+
return {
|
|
93
|
+
restrict: 'A',
|
|
94
|
+
require: 'ngModel',
|
|
95
|
+
compile: function postLink(element, attr) {
|
|
96
|
+
element.attr('data-toggle', 'buttons');
|
|
97
|
+
element.removeAttr('ng-model');
|
|
98
|
+
var children = element[0].querySelectorAll('input[type="radio"]');
|
|
99
|
+
angular.forEach(children, function (child) {
|
|
100
|
+
angular.element(child).attr('bs-radio', '');
|
|
101
|
+
angular.element(child).attr('ng-model', attr.ngModel);
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
}).directive('bsRadio', [
|
|
106
|
+
'$button',
|
|
107
|
+
'$$rAF',
|
|
108
|
+
function ($button, $$rAF) {
|
|
109
|
+
var defaults = $button.defaults;
|
|
110
|
+
var constantValueRegExp = /^(true|false|\d+)$/;
|
|
111
|
+
return {
|
|
112
|
+
restrict: 'A',
|
|
113
|
+
require: 'ngModel',
|
|
114
|
+
link: function postLink(scope, element, attr, controller) {
|
|
115
|
+
var options = defaults;
|
|
116
|
+
// Support `label > input[type="radio"]` markup
|
|
117
|
+
var isInput = element[0].nodeName === 'INPUT';
|
|
118
|
+
var activeElement = isInput ? element.parent() : element;
|
|
119
|
+
var value = constantValueRegExp.test(attr.value) ? scope.$eval(attr.value) : attr.value;
|
|
120
|
+
// model -> view
|
|
121
|
+
controller.$render = function () {
|
|
122
|
+
// console.warn('$render', element.attr('value'), 'controller.$modelValue', typeof controller.$modelValue, controller.$modelValue, 'controller.$viewValue', typeof controller.$viewValue, controller.$viewValue);
|
|
123
|
+
var isActive = angular.equals(controller.$modelValue, value);
|
|
124
|
+
$$rAF(function () {
|
|
125
|
+
if (isInput)
|
|
126
|
+
element[0].checked = isActive;
|
|
127
|
+
activeElement.toggleClass(options.activeClass, isActive);
|
|
128
|
+
});
|
|
129
|
+
};
|
|
130
|
+
// view -> model
|
|
131
|
+
element.bind(options.toggleEvent, function () {
|
|
132
|
+
scope.$apply(function () {
|
|
133
|
+
// console.warn('!click', element.attr('value'), 'controller.$viewValue', typeof controller.$viewValue, controller.$viewValue, 'controller.$modelValue', typeof controller.$modelValue, controller.$modelValue);
|
|
134
|
+
controller.$setViewValue(value);
|
|
135
|
+
controller.$render();
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
]);
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* angular-strap
|
|
3
|
+
* @version v2.0.1 - 2014-04-10
|
|
4
|
+
* @link http://mgcrea.github.io/angular-strap
|
|
5
|
+
* @author Olivier Louvignes (olivier@mg-crea.com)
|
|
6
|
+
* @license MIT License, http://www.opensource.org/licenses/MIT
|
|
7
|
+
*/
|
|
8
|
+
'use strict';
|
|
9
|
+
angular.module('mgcrea.ngStrap.helpers.dateParser', []).provider('$dateParser', [
|
|
10
|
+
'$localeProvider',
|
|
11
|
+
function ($localeProvider) {
|
|
12
|
+
var proto = Date.prototype;
|
|
13
|
+
function isNumeric(n) {
|
|
14
|
+
return !isNaN(parseFloat(n)) && isFinite(n);
|
|
15
|
+
}
|
|
16
|
+
var defaults = this.defaults = {
|
|
17
|
+
format: 'shortDate',
|
|
18
|
+
strict: false
|
|
19
|
+
};
|
|
20
|
+
this.$get = [
|
|
21
|
+
'$locale',
|
|
22
|
+
function ($locale) {
|
|
23
|
+
var DateParserFactory = function (config) {
|
|
24
|
+
var options = angular.extend({}, defaults, config);
|
|
25
|
+
var $dateParser = {};
|
|
26
|
+
var regExpMap = {
|
|
27
|
+
'sss': '[0-9]{3}',
|
|
28
|
+
'ss': '[0-5][0-9]',
|
|
29
|
+
's': options.strict ? '[1-5]?[0-9]' : '[0-9]|[0-5][0-9]',
|
|
30
|
+
'mm': '[0-5][0-9]',
|
|
31
|
+
'm': options.strict ? '[1-5]?[0-9]' : '[0-9]|[0-5][0-9]',
|
|
32
|
+
'HH': '[01][0-9]|2[0-3]',
|
|
33
|
+
'H': options.strict ? '1?[0-9]|2[0-3]' : '[01]?[0-9]|2[0-3]',
|
|
34
|
+
'hh': '[0][1-9]|[1][012]',
|
|
35
|
+
'h': options.strict ? '[1-9]|1[012]' : '0?[1-9]|1[012]',
|
|
36
|
+
'a': 'AM|PM',
|
|
37
|
+
'EEEE': $locale.DATETIME_FORMATS.DAY.join('|'),
|
|
38
|
+
'EEE': $locale.DATETIME_FORMATS.SHORTDAY.join('|'),
|
|
39
|
+
'dd': '0[1-9]|[12][0-9]|3[01]',
|
|
40
|
+
'd': options.strict ? '[1-9]|[1-2][0-9]|3[01]' : '0?[1-9]|[1-2][0-9]|3[01]',
|
|
41
|
+
'MMMM': $locale.DATETIME_FORMATS.MONTH.join('|'),
|
|
42
|
+
'MMM': $locale.DATETIME_FORMATS.SHORTMONTH.join('|'),
|
|
43
|
+
'MM': '0[1-9]|1[012]',
|
|
44
|
+
'M': options.strict ? '[1-9]|1[012]' : '0?[1-9]|1[012]',
|
|
45
|
+
'yyyy': '[1]{1}[0-9]{3}|[2]{1}[0-9]{3}',
|
|
46
|
+
'yy': '[0-9]{2}',
|
|
47
|
+
'y': options.strict ? '-?(0|[1-9][0-9]{0,3})' : '-?0*[0-9]{1,4}'
|
|
48
|
+
};
|
|
49
|
+
var setFnMap = {
|
|
50
|
+
'sss': proto.setMilliseconds,
|
|
51
|
+
'ss': proto.setSeconds,
|
|
52
|
+
's': proto.setSeconds,
|
|
53
|
+
'mm': proto.setMinutes,
|
|
54
|
+
'm': proto.setMinutes,
|
|
55
|
+
'HH': proto.setHours,
|
|
56
|
+
'H': proto.setHours,
|
|
57
|
+
'hh': proto.setHours,
|
|
58
|
+
'h': proto.setHours,
|
|
59
|
+
'dd': proto.setDate,
|
|
60
|
+
'd': proto.setDate,
|
|
61
|
+
'a': function (value) {
|
|
62
|
+
var hours = this.getHours();
|
|
63
|
+
return this.setHours(value.match(/pm/i) ? hours + 12 : hours);
|
|
64
|
+
},
|
|
65
|
+
'MMMM': function (value) {
|
|
66
|
+
return this.setMonth($locale.DATETIME_FORMATS.MONTH.indexOf(value));
|
|
67
|
+
},
|
|
68
|
+
'MMM': function (value) {
|
|
69
|
+
return this.setMonth($locale.DATETIME_FORMATS.SHORTMONTH.indexOf(value));
|
|
70
|
+
},
|
|
71
|
+
'MM': function (value) {
|
|
72
|
+
return this.setMonth(1 * value - 1);
|
|
73
|
+
},
|
|
74
|
+
'M': function (value) {
|
|
75
|
+
return this.setMonth(1 * value - 1);
|
|
76
|
+
},
|
|
77
|
+
'yyyy': proto.setFullYear,
|
|
78
|
+
'yy': function (value) {
|
|
79
|
+
return this.setFullYear(2000 + 1 * value);
|
|
80
|
+
},
|
|
81
|
+
'y': proto.setFullYear
|
|
82
|
+
};
|
|
83
|
+
var regex, setMap;
|
|
84
|
+
$dateParser.init = function () {
|
|
85
|
+
$dateParser.$format = $locale.DATETIME_FORMATS[options.format] || options.format;
|
|
86
|
+
regex = regExpForFormat($dateParser.$format);
|
|
87
|
+
setMap = setMapForFormat($dateParser.$format);
|
|
88
|
+
};
|
|
89
|
+
$dateParser.isValid = function (date) {
|
|
90
|
+
if (angular.isDate(date))
|
|
91
|
+
return !isNaN(date.getTime());
|
|
92
|
+
return regex.test(date);
|
|
93
|
+
};
|
|
94
|
+
$dateParser.parse = function (value, baseDate) {
|
|
95
|
+
if (angular.isDate(value))
|
|
96
|
+
return value;
|
|
97
|
+
var matches = regex.exec(value);
|
|
98
|
+
if (!matches)
|
|
99
|
+
return false;
|
|
100
|
+
var date = baseDate || new Date(0);
|
|
101
|
+
for (var i = 0; i < matches.length - 1; i++) {
|
|
102
|
+
setMap[i] && setMap[i].call(date, matches[i + 1]);
|
|
103
|
+
}
|
|
104
|
+
return date;
|
|
105
|
+
};
|
|
106
|
+
// Private functions
|
|
107
|
+
function setMapForFormat(format) {
|
|
108
|
+
var keys = Object.keys(setFnMap), i;
|
|
109
|
+
var map = [], sortedMap = [];
|
|
110
|
+
// Map to setFn
|
|
111
|
+
var clonedFormat = format;
|
|
112
|
+
for (i = 0; i < keys.length; i++) {
|
|
113
|
+
if (format.split(keys[i]).length > 1) {
|
|
114
|
+
var index = clonedFormat.search(keys[i]);
|
|
115
|
+
format = format.split(keys[i]).join('');
|
|
116
|
+
if (setFnMap[keys[i]])
|
|
117
|
+
map[index] = setFnMap[keys[i]];
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// Sort result map
|
|
121
|
+
angular.forEach(map, function (v) {
|
|
122
|
+
sortedMap.push(v);
|
|
123
|
+
});
|
|
124
|
+
return sortedMap;
|
|
125
|
+
}
|
|
126
|
+
function escapeReservedSymbols(text) {
|
|
127
|
+
return text.replace(/\//g, '[\\/]').replace('/-/g', '[-]').replace(/\./g, '[.]').replace(/\\s/g, '[\\s]');
|
|
128
|
+
}
|
|
129
|
+
function regExpForFormat(format) {
|
|
130
|
+
var keys = Object.keys(regExpMap), i;
|
|
131
|
+
var re = format;
|
|
132
|
+
// Abstract replaces to avoid collisions
|
|
133
|
+
for (i = 0; i < keys.length; i++) {
|
|
134
|
+
re = re.split(keys[i]).join('${' + i + '}');
|
|
135
|
+
}
|
|
136
|
+
// Replace abstracted values
|
|
137
|
+
for (i = 0; i < keys.length; i++) {
|
|
138
|
+
re = re.split('${' + i + '}').join('(' + regExpMap[keys[i]] + ')');
|
|
139
|
+
}
|
|
140
|
+
format = escapeReservedSymbols(format);
|
|
141
|
+
return new RegExp('^' + re + '$', ['i']);
|
|
142
|
+
}
|
|
143
|
+
$dateParser.init();
|
|
144
|
+
return $dateParser;
|
|
145
|
+
};
|
|
146
|
+
return DateParserFactory;
|
|
147
|
+
}
|
|
148
|
+
];
|
|
149
|
+
}
|
|
150
|
+
]);
|
|
@@ -0,0 +1,583 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* angular-strap
|
|
3
|
+
* @version v2.0.1 - 2014-04-10
|
|
4
|
+
* @link http://mgcrea.github.io/angular-strap
|
|
5
|
+
* @author Olivier Louvignes (olivier@mg-crea.com)
|
|
6
|
+
* @license MIT License, http://www.opensource.org/licenses/MIT
|
|
7
|
+
*/
|
|
8
|
+
'use strict';
|
|
9
|
+
angular.module('mgcrea.ngStrap.datepicker', [
|
|
10
|
+
'mgcrea.ngStrap.helpers.dateParser',
|
|
11
|
+
'mgcrea.ngStrap.tooltip'
|
|
12
|
+
]).provider('$datepicker', function () {
|
|
13
|
+
var defaults = this.defaults = {
|
|
14
|
+
animation: 'am-fade',
|
|
15
|
+
prefixClass: 'datepicker',
|
|
16
|
+
placement: 'bottom-left',
|
|
17
|
+
template: 'datepicker/datepicker.tpl.html',
|
|
18
|
+
trigger: 'focus',
|
|
19
|
+
container: false,
|
|
20
|
+
keyboard: true,
|
|
21
|
+
html: false,
|
|
22
|
+
delay: 0,
|
|
23
|
+
useNative: false,
|
|
24
|
+
dateType: 'date',
|
|
25
|
+
dateFormat: 'shortDate',
|
|
26
|
+
strictFormat: false,
|
|
27
|
+
autoclose: false,
|
|
28
|
+
minDate: -Infinity,
|
|
29
|
+
maxDate: +Infinity,
|
|
30
|
+
startView: 0,
|
|
31
|
+
minView: 0,
|
|
32
|
+
startWeek: 0
|
|
33
|
+
};
|
|
34
|
+
this.$get = [
|
|
35
|
+
'$window',
|
|
36
|
+
'$document',
|
|
37
|
+
'$rootScope',
|
|
38
|
+
'$sce',
|
|
39
|
+
'$locale',
|
|
40
|
+
'dateFilter',
|
|
41
|
+
'datepickerViews',
|
|
42
|
+
'$tooltip',
|
|
43
|
+
function ($window, $document, $rootScope, $sce, $locale, dateFilter, datepickerViews, $tooltip) {
|
|
44
|
+
var bodyEl = angular.element($window.document.body);
|
|
45
|
+
var isTouch = 'createTouch' in $window.document;
|
|
46
|
+
var isNative = /(ip(a|o)d|iphone|android)/gi.test($window.navigator.userAgent);
|
|
47
|
+
if (!defaults.lang)
|
|
48
|
+
defaults.lang = $locale.id;
|
|
49
|
+
function DatepickerFactory(element, controller, config) {
|
|
50
|
+
var $datepicker = $tooltip(element, angular.extend({}, defaults, config));
|
|
51
|
+
var parentScope = config.scope;
|
|
52
|
+
var options = $datepicker.$options;
|
|
53
|
+
var scope = $datepicker.$scope;
|
|
54
|
+
if (options.startView)
|
|
55
|
+
options.startView -= options.minView;
|
|
56
|
+
// View vars
|
|
57
|
+
var pickerViews = datepickerViews($datepicker);
|
|
58
|
+
$datepicker.$views = pickerViews.views;
|
|
59
|
+
var viewDate = pickerViews.viewDate;
|
|
60
|
+
scope.$mode = options.startView;
|
|
61
|
+
var $picker = $datepicker.$views[scope.$mode];
|
|
62
|
+
// Scope methods
|
|
63
|
+
scope.$select = function (date) {
|
|
64
|
+
$datepicker.select(date);
|
|
65
|
+
};
|
|
66
|
+
scope.$selectPane = function (value) {
|
|
67
|
+
$datepicker.$selectPane(value);
|
|
68
|
+
};
|
|
69
|
+
scope.$toggleMode = function () {
|
|
70
|
+
$datepicker.setMode((scope.$mode + 1) % $datepicker.$views.length);
|
|
71
|
+
};
|
|
72
|
+
// Public methods
|
|
73
|
+
$datepicker.update = function (date) {
|
|
74
|
+
// console.warn('$datepicker.update() newValue=%o', date);
|
|
75
|
+
if (angular.isDate(date) && !isNaN(date.getTime())) {
|
|
76
|
+
$datepicker.$date = date;
|
|
77
|
+
$picker.update.call($picker, date);
|
|
78
|
+
}
|
|
79
|
+
// Build only if pristine
|
|
80
|
+
$datepicker.$build(true);
|
|
81
|
+
};
|
|
82
|
+
$datepicker.select = function (date, keep) {
|
|
83
|
+
// console.warn('$datepicker.select', date, scope.$mode);
|
|
84
|
+
if (!angular.isDate(controller.$dateValue))
|
|
85
|
+
controller.$dateValue = new Date(date);
|
|
86
|
+
controller.$dateValue.setFullYear(date.getFullYear(), date.getMonth(), date.getDate());
|
|
87
|
+
if (!scope.$mode || keep) {
|
|
88
|
+
controller.$setViewValue(controller.$dateValue);
|
|
89
|
+
controller.$render();
|
|
90
|
+
if (options.autoclose && !keep) {
|
|
91
|
+
$datepicker.hide(true);
|
|
92
|
+
}
|
|
93
|
+
} else {
|
|
94
|
+
angular.extend(viewDate, {
|
|
95
|
+
year: date.getFullYear(),
|
|
96
|
+
month: date.getMonth(),
|
|
97
|
+
date: date.getDate()
|
|
98
|
+
});
|
|
99
|
+
$datepicker.setMode(scope.$mode - 1);
|
|
100
|
+
$datepicker.$build();
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
$datepicker.setMode = function (mode) {
|
|
104
|
+
// console.warn('$datepicker.setMode', mode);
|
|
105
|
+
scope.$mode = mode;
|
|
106
|
+
$picker = $datepicker.$views[scope.$mode];
|
|
107
|
+
$datepicker.$build();
|
|
108
|
+
};
|
|
109
|
+
// Protected methods
|
|
110
|
+
$datepicker.$build = function (pristine) {
|
|
111
|
+
// console.warn('$datepicker.$build() viewDate=%o', viewDate);
|
|
112
|
+
if (pristine === true && $picker.built)
|
|
113
|
+
return;
|
|
114
|
+
if (pristine === false && !$picker.built)
|
|
115
|
+
return;
|
|
116
|
+
$picker.build.call($picker);
|
|
117
|
+
};
|
|
118
|
+
$datepicker.$updateSelected = function () {
|
|
119
|
+
for (var i = 0, l = scope.rows.length; i < l; i++) {
|
|
120
|
+
angular.forEach(scope.rows[i], updateSelected);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
$datepicker.$isSelected = function (date) {
|
|
124
|
+
return $picker.isSelected(date);
|
|
125
|
+
};
|
|
126
|
+
$datepicker.$selectPane = function (value) {
|
|
127
|
+
var steps = $picker.steps;
|
|
128
|
+
var targetDate = new Date(Date.UTC(viewDate.year + (steps.year || 0) * value, viewDate.month + (steps.month || 0) * value, viewDate.date + (steps.day || 0) * value));
|
|
129
|
+
angular.extend(viewDate, {
|
|
130
|
+
year: targetDate.getUTCFullYear(),
|
|
131
|
+
month: targetDate.getUTCMonth(),
|
|
132
|
+
date: targetDate.getUTCDate()
|
|
133
|
+
});
|
|
134
|
+
$datepicker.$build();
|
|
135
|
+
};
|
|
136
|
+
$datepicker.$onMouseDown = function (evt) {
|
|
137
|
+
// Prevent blur on mousedown on .dropdown-menu
|
|
138
|
+
evt.preventDefault();
|
|
139
|
+
evt.stopPropagation();
|
|
140
|
+
// Emulate click for mobile devices
|
|
141
|
+
if (isTouch) {
|
|
142
|
+
var targetEl = angular.element(evt.target);
|
|
143
|
+
if (targetEl[0].nodeName.toLowerCase() !== 'button') {
|
|
144
|
+
targetEl = targetEl.parent();
|
|
145
|
+
}
|
|
146
|
+
targetEl.triggerHandler('click');
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
$datepicker.$onKeyDown = function (evt) {
|
|
150
|
+
if (!/(38|37|39|40|13)/.test(evt.keyCode) || evt.shiftKey || evt.altKey)
|
|
151
|
+
return;
|
|
152
|
+
evt.preventDefault();
|
|
153
|
+
evt.stopPropagation();
|
|
154
|
+
if (evt.keyCode === 13) {
|
|
155
|
+
if (!scope.$mode) {
|
|
156
|
+
return $datepicker.hide(true);
|
|
157
|
+
} else {
|
|
158
|
+
return scope.$apply(function () {
|
|
159
|
+
$datepicker.setMode(scope.$mode - 1);
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// Navigate with keyboard
|
|
164
|
+
$picker.onKeyDown(evt);
|
|
165
|
+
parentScope.$digest();
|
|
166
|
+
};
|
|
167
|
+
// Private
|
|
168
|
+
function updateSelected(el) {
|
|
169
|
+
el.selected = $datepicker.$isSelected(el.date);
|
|
170
|
+
}
|
|
171
|
+
function focusElement() {
|
|
172
|
+
element[0].focus();
|
|
173
|
+
}
|
|
174
|
+
// Overrides
|
|
175
|
+
var _init = $datepicker.init;
|
|
176
|
+
$datepicker.init = function () {
|
|
177
|
+
if (isNative && options.useNative) {
|
|
178
|
+
element.prop('type', 'date');
|
|
179
|
+
element.css('-webkit-appearance', 'textfield');
|
|
180
|
+
return;
|
|
181
|
+
} else if (isTouch) {
|
|
182
|
+
element.prop('type', 'text');
|
|
183
|
+
element.attr('readonly', 'true');
|
|
184
|
+
element.on('click', focusElement);
|
|
185
|
+
}
|
|
186
|
+
_init();
|
|
187
|
+
};
|
|
188
|
+
var _destroy = $datepicker.destroy;
|
|
189
|
+
$datepicker.destroy = function () {
|
|
190
|
+
if (isNative && options.useNative) {
|
|
191
|
+
element.off('click', focusElement);
|
|
192
|
+
}
|
|
193
|
+
_destroy();
|
|
194
|
+
};
|
|
195
|
+
var _show = $datepicker.show;
|
|
196
|
+
$datepicker.show = function () {
|
|
197
|
+
_show();
|
|
198
|
+
setTimeout(function () {
|
|
199
|
+
$datepicker.$element.on(isTouch ? 'touchstart' : 'mousedown', $datepicker.$onMouseDown);
|
|
200
|
+
if (options.keyboard) {
|
|
201
|
+
element.on('keydown', $datepicker.$onKeyDown);
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
};
|
|
205
|
+
var _hide = $datepicker.hide;
|
|
206
|
+
$datepicker.hide = function (blur) {
|
|
207
|
+
$datepicker.$element.off(isTouch ? 'touchstart' : 'mousedown', $datepicker.$onMouseDown);
|
|
208
|
+
if (options.keyboard) {
|
|
209
|
+
element.off('keydown', $datepicker.$onKeyDown);
|
|
210
|
+
}
|
|
211
|
+
_hide(blur);
|
|
212
|
+
};
|
|
213
|
+
return $datepicker;
|
|
214
|
+
}
|
|
215
|
+
DatepickerFactory.defaults = defaults;
|
|
216
|
+
return DatepickerFactory;
|
|
217
|
+
}
|
|
218
|
+
];
|
|
219
|
+
}).directive('bsDatepicker', [
|
|
220
|
+
'$window',
|
|
221
|
+
'$parse',
|
|
222
|
+
'$q',
|
|
223
|
+
'$locale',
|
|
224
|
+
'dateFilter',
|
|
225
|
+
'$datepicker',
|
|
226
|
+
'$dateParser',
|
|
227
|
+
'$timeout',
|
|
228
|
+
function ($window, $parse, $q, $locale, dateFilter, $datepicker, $dateParser, $timeout) {
|
|
229
|
+
var defaults = $datepicker.defaults;
|
|
230
|
+
var isNative = /(ip(a|o)d|iphone|android)/gi.test($window.navigator.userAgent);
|
|
231
|
+
var isNumeric = function (n) {
|
|
232
|
+
return !isNaN(parseFloat(n)) && isFinite(n);
|
|
233
|
+
};
|
|
234
|
+
return {
|
|
235
|
+
restrict: 'EAC',
|
|
236
|
+
require: 'ngModel',
|
|
237
|
+
link: function postLink(scope, element, attr, controller) {
|
|
238
|
+
// Directive options
|
|
239
|
+
var options = {
|
|
240
|
+
scope: scope,
|
|
241
|
+
controller: controller
|
|
242
|
+
};
|
|
243
|
+
angular.forEach([
|
|
244
|
+
'placement',
|
|
245
|
+
'container',
|
|
246
|
+
'delay',
|
|
247
|
+
'trigger',
|
|
248
|
+
'keyboard',
|
|
249
|
+
'html',
|
|
250
|
+
'animation',
|
|
251
|
+
'template',
|
|
252
|
+
'autoclose',
|
|
253
|
+
'dateType',
|
|
254
|
+
'dateFormat',
|
|
255
|
+
'strictFormat',
|
|
256
|
+
'startWeek',
|
|
257
|
+
'useNative',
|
|
258
|
+
'lang',
|
|
259
|
+
'startView',
|
|
260
|
+
'minView'
|
|
261
|
+
], function (key) {
|
|
262
|
+
if (angular.isDefined(attr[key]))
|
|
263
|
+
options[key] = attr[key];
|
|
264
|
+
});
|
|
265
|
+
// Initialize datepicker
|
|
266
|
+
if (isNative && options.useNative)
|
|
267
|
+
options.dateFormat = 'yyyy-MM-dd';
|
|
268
|
+
var datepicker = $datepicker(element, controller, options);
|
|
269
|
+
options = datepicker.$options;
|
|
270
|
+
// Observe attributes for changes
|
|
271
|
+
angular.forEach([
|
|
272
|
+
'minDate',
|
|
273
|
+
'maxDate'
|
|
274
|
+
], function (key) {
|
|
275
|
+
// console.warn('attr.$observe(%s)', key, attr[key]);
|
|
276
|
+
angular.isDefined(attr[key]) && attr.$observe(key, function (newValue) {
|
|
277
|
+
// console.warn('attr.$observe(%s)=%o', key, newValue);
|
|
278
|
+
if (newValue === 'today') {
|
|
279
|
+
var today = new Date();
|
|
280
|
+
datepicker.$options[key] = +new Date(today.getFullYear(), today.getMonth(), today.getDate() + (key === 'maxDate' ? 1 : 0), 0, 0, 0, key === 'minDate' ? 0 : -1);
|
|
281
|
+
} else if (angular.isString(newValue) && newValue.match(/^".+"$/)) {
|
|
282
|
+
// Support {{ dateObj }}
|
|
283
|
+
datepicker.$options[key] = +new Date(newValue.substr(1, newValue.length - 2));
|
|
284
|
+
} else if (isNumeric(newValue)) {
|
|
285
|
+
datepicker.$options[key] = +new Date(parseInt(newValue, 10));
|
|
286
|
+
} else {
|
|
287
|
+
datepicker.$options[key] = +new Date(newValue);
|
|
288
|
+
}
|
|
289
|
+
// Build only if dirty
|
|
290
|
+
!isNaN(datepicker.$options[key]) && datepicker.$build(false);
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
// Watch model for changes
|
|
294
|
+
scope.$watch(attr.ngModel, function (newValue, oldValue) {
|
|
295
|
+
datepicker.update(controller.$dateValue);
|
|
296
|
+
}, true);
|
|
297
|
+
var dateParser = $dateParser({
|
|
298
|
+
format: options.dateFormat,
|
|
299
|
+
lang: options.lang,
|
|
300
|
+
strict: options.strictFormat
|
|
301
|
+
});
|
|
302
|
+
// viewValue -> $parsers -> modelValue
|
|
303
|
+
controller.$parsers.unshift(function (viewValue) {
|
|
304
|
+
// console.warn('$parser("%s"): viewValue=%o', element.attr('ng-model'), viewValue);
|
|
305
|
+
// Null values should correctly reset the model value & validity
|
|
306
|
+
if (!viewValue) {
|
|
307
|
+
controller.$setValidity('date', true);
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
var parsedDate = dateParser.parse(viewValue, controller.$dateValue);
|
|
311
|
+
if (!parsedDate || isNaN(parsedDate.getTime())) {
|
|
312
|
+
controller.$setValidity('date', false);
|
|
313
|
+
return;
|
|
314
|
+
} else {
|
|
315
|
+
var isValid = (isNaN(datepicker.$options.minDate) || parsedDate.getTime() >= datepicker.$options.minDate) && (isNaN(datepicker.$options.maxDate) || parsedDate.getTime() <= datepicker.$options.maxDate);
|
|
316
|
+
controller.$setValidity('date', isValid);
|
|
317
|
+
// Only update the model when we have a valid date
|
|
318
|
+
if (isValid)
|
|
319
|
+
controller.$dateValue = parsedDate;
|
|
320
|
+
}
|
|
321
|
+
if (options.dateType === 'string') {
|
|
322
|
+
return dateFilter(viewValue, options.dateFormat);
|
|
323
|
+
} else if (options.dateType === 'number') {
|
|
324
|
+
return controller.$dateValue.getTime();
|
|
325
|
+
} else if (options.dateType === 'iso') {
|
|
326
|
+
return controller.$dateValue.toISOString();
|
|
327
|
+
} else {
|
|
328
|
+
return new Date(controller.$dateValue);
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
// modelValue -> $formatters -> viewValue
|
|
332
|
+
controller.$formatters.push(function (modelValue) {
|
|
333
|
+
// console.warn('$formatter("%s"): modelValue=%o (%o)', element.attr('ng-model'), modelValue, typeof modelValue);
|
|
334
|
+
var date;
|
|
335
|
+
if (angular.isUndefined(modelValue) || modelValue === null) {
|
|
336
|
+
date = NaN;
|
|
337
|
+
} else if (angular.isDate(modelValue)) {
|
|
338
|
+
date = modelValue;
|
|
339
|
+
} else if (options.dateType === 'string') {
|
|
340
|
+
date = dateParser.parse(modelValue);
|
|
341
|
+
} else {
|
|
342
|
+
date = new Date(modelValue);
|
|
343
|
+
}
|
|
344
|
+
// Setup default value?
|
|
345
|
+
// if(isNaN(date.getTime())) {
|
|
346
|
+
// var today = new Date();
|
|
347
|
+
// date = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 0, 0, 0, 0);
|
|
348
|
+
// }
|
|
349
|
+
controller.$dateValue = date;
|
|
350
|
+
return controller.$dateValue;
|
|
351
|
+
});
|
|
352
|
+
// viewValue -> element
|
|
353
|
+
controller.$render = function () {
|
|
354
|
+
// console.warn('$render("%s"): viewValue=%o', element.attr('ng-model'), controller.$viewValue);
|
|
355
|
+
element.val(!controller.$dateValue || isNaN(controller.$dateValue.getTime()) ? '' : dateFilter(controller.$dateValue, options.dateFormat));
|
|
356
|
+
};
|
|
357
|
+
// Garbage collection
|
|
358
|
+
scope.$on('$destroy', function () {
|
|
359
|
+
datepicker.destroy();
|
|
360
|
+
options = null;
|
|
361
|
+
datepicker = null;
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
]).provider('datepickerViews', function () {
|
|
367
|
+
var defaults = this.defaults = {
|
|
368
|
+
dayFormat: 'dd',
|
|
369
|
+
daySplit: 7
|
|
370
|
+
};
|
|
371
|
+
// Split array into smaller arrays
|
|
372
|
+
function split(arr, size) {
|
|
373
|
+
var arrays = [];
|
|
374
|
+
while (arr.length > 0) {
|
|
375
|
+
arrays.push(arr.splice(0, size));
|
|
376
|
+
}
|
|
377
|
+
return arrays;
|
|
378
|
+
}
|
|
379
|
+
// Modulus operator
|
|
380
|
+
function mod(n, m) {
|
|
381
|
+
return (n % m + m) % m;
|
|
382
|
+
}
|
|
383
|
+
this.$get = [
|
|
384
|
+
'$locale',
|
|
385
|
+
'$sce',
|
|
386
|
+
'dateFilter',
|
|
387
|
+
function ($locale, $sce, dateFilter) {
|
|
388
|
+
return function (picker) {
|
|
389
|
+
var scope = picker.$scope;
|
|
390
|
+
var options = picker.$options;
|
|
391
|
+
var weekDaysMin = $locale.DATETIME_FORMATS.SHORTDAY;
|
|
392
|
+
var weekDaysLabels = weekDaysMin.slice(options.startWeek).concat(weekDaysMin.slice(0, options.startWeek));
|
|
393
|
+
var weekDaysLabelsHtml = $sce.trustAsHtml('<th class="dow text-center">' + weekDaysLabels.join('</th><th class="dow text-center">') + '</th>');
|
|
394
|
+
var startDate = picker.$date || new Date();
|
|
395
|
+
var viewDate = {
|
|
396
|
+
year: startDate.getFullYear(),
|
|
397
|
+
month: startDate.getMonth(),
|
|
398
|
+
date: startDate.getDate()
|
|
399
|
+
};
|
|
400
|
+
var timezoneOffset = startDate.getTimezoneOffset() * 60000;
|
|
401
|
+
var views = [
|
|
402
|
+
{
|
|
403
|
+
format: 'dd',
|
|
404
|
+
split: 7,
|
|
405
|
+
steps: { month: 1 },
|
|
406
|
+
update: function (date, force) {
|
|
407
|
+
if (!this.built || force || date.getFullYear() !== viewDate.year || date.getMonth() !== viewDate.month) {
|
|
408
|
+
angular.extend(viewDate, {
|
|
409
|
+
year: picker.$date.getFullYear(),
|
|
410
|
+
month: picker.$date.getMonth(),
|
|
411
|
+
date: picker.$date.getDate()
|
|
412
|
+
});
|
|
413
|
+
picker.$build();
|
|
414
|
+
} else if (date.getDate() !== viewDate.date) {
|
|
415
|
+
viewDate.date = picker.$date.getDate();
|
|
416
|
+
picker.$updateSelected();
|
|
417
|
+
}
|
|
418
|
+
},
|
|
419
|
+
build: function () {
|
|
420
|
+
var firstDayOfMonth = new Date(viewDate.year, viewDate.month, 1), firstDayOfMonthOffset = firstDayOfMonth.getTimezoneOffset();
|
|
421
|
+
var firstDate = new Date(+firstDayOfMonth - mod(firstDayOfMonth.getDay() - options.startWeek, 6) * 86400000), firstDateOffset = firstDate.getTimezoneOffset();
|
|
422
|
+
// Handle daylight time switch
|
|
423
|
+
if (firstDateOffset !== firstDayOfMonthOffset)
|
|
424
|
+
firstDate = new Date(+firstDate + (firstDateOffset - firstDayOfMonthOffset) * 60000);
|
|
425
|
+
var days = [], day;
|
|
426
|
+
for (var i = 0; i < 42; i++) {
|
|
427
|
+
// < 7 * 6
|
|
428
|
+
day = new Date(firstDate.getFullYear(), firstDate.getMonth(), firstDate.getDate() + i);
|
|
429
|
+
days.push({
|
|
430
|
+
date: day,
|
|
431
|
+
label: dateFilter(day, this.format),
|
|
432
|
+
selected: picker.$date && this.isSelected(day),
|
|
433
|
+
muted: day.getMonth() !== viewDate.month,
|
|
434
|
+
disabled: this.isDisabled(day)
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
scope.title = dateFilter(firstDayOfMonth, 'MMMM yyyy');
|
|
438
|
+
scope.labels = weekDaysLabelsHtml;
|
|
439
|
+
scope.rows = split(days, this.split);
|
|
440
|
+
this.built = true;
|
|
441
|
+
},
|
|
442
|
+
isSelected: function (date) {
|
|
443
|
+
return picker.$date && date.getFullYear() === picker.$date.getFullYear() && date.getMonth() === picker.$date.getMonth() && date.getDate() === picker.$date.getDate();
|
|
444
|
+
},
|
|
445
|
+
isDisabled: function (date) {
|
|
446
|
+
return date.getTime() < options.minDate || date.getTime() > options.maxDate;
|
|
447
|
+
},
|
|
448
|
+
onKeyDown: function (evt) {
|
|
449
|
+
var actualTime = picker.$date.getTime();
|
|
450
|
+
if (evt.keyCode === 37)
|
|
451
|
+
picker.select(new Date(actualTime - 1 * 86400000), true);
|
|
452
|
+
else if (evt.keyCode === 38)
|
|
453
|
+
picker.select(new Date(actualTime - 7 * 86400000), true);
|
|
454
|
+
else if (evt.keyCode === 39)
|
|
455
|
+
picker.select(new Date(actualTime + 1 * 86400000), true);
|
|
456
|
+
else if (evt.keyCode === 40)
|
|
457
|
+
picker.select(new Date(actualTime + 7 * 86400000), true);
|
|
458
|
+
}
|
|
459
|
+
},
|
|
460
|
+
{
|
|
461
|
+
name: 'month',
|
|
462
|
+
format: 'MMM',
|
|
463
|
+
split: 4,
|
|
464
|
+
steps: { year: 1 },
|
|
465
|
+
update: function (date, force) {
|
|
466
|
+
if (!this.built || date.getFullYear() !== viewDate.year) {
|
|
467
|
+
angular.extend(viewDate, {
|
|
468
|
+
year: picker.$date.getFullYear(),
|
|
469
|
+
month: picker.$date.getMonth(),
|
|
470
|
+
date: picker.$date.getDate()
|
|
471
|
+
});
|
|
472
|
+
picker.$build();
|
|
473
|
+
} else if (date.getMonth() !== viewDate.month) {
|
|
474
|
+
angular.extend(viewDate, {
|
|
475
|
+
month: picker.$date.getMonth(),
|
|
476
|
+
date: picker.$date.getDate()
|
|
477
|
+
});
|
|
478
|
+
picker.$updateSelected();
|
|
479
|
+
}
|
|
480
|
+
},
|
|
481
|
+
build: function () {
|
|
482
|
+
var firstMonth = new Date(viewDate.year, 0, 1);
|
|
483
|
+
var months = [], month;
|
|
484
|
+
for (var i = 0; i < 12; i++) {
|
|
485
|
+
month = new Date(viewDate.year, i, 1);
|
|
486
|
+
months.push({
|
|
487
|
+
date: month,
|
|
488
|
+
label: dateFilter(month, this.format),
|
|
489
|
+
selected: picker.$isSelected(month),
|
|
490
|
+
disabled: this.isDisabled(month)
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
scope.title = dateFilter(month, 'yyyy');
|
|
494
|
+
scope.labels = false;
|
|
495
|
+
scope.rows = split(months, this.split);
|
|
496
|
+
this.built = true;
|
|
497
|
+
},
|
|
498
|
+
isSelected: function (date) {
|
|
499
|
+
return picker.$date && date.getFullYear() === picker.$date.getFullYear() && date.getMonth() === picker.$date.getMonth();
|
|
500
|
+
},
|
|
501
|
+
isDisabled: function (date) {
|
|
502
|
+
var lastDate = +new Date(date.getFullYear(), date.getMonth() + 1, 0);
|
|
503
|
+
return lastDate < options.minDate || date.getTime() > options.maxDate;
|
|
504
|
+
},
|
|
505
|
+
onKeyDown: function (evt) {
|
|
506
|
+
var actualMonth = picker.$date.getMonth();
|
|
507
|
+
if (evt.keyCode === 37)
|
|
508
|
+
picker.select(new Date(picker.$date.setMonth(actualMonth - 1)), true);
|
|
509
|
+
else if (evt.keyCode === 38)
|
|
510
|
+
picker.select(new Date(picker.$date.setMonth(actualMonth - 4)), true);
|
|
511
|
+
else if (evt.keyCode === 39)
|
|
512
|
+
picker.select(new Date(picker.$date.setMonth(actualMonth + 1)), true);
|
|
513
|
+
else if (evt.keyCode === 40)
|
|
514
|
+
picker.select(new Date(picker.$date.setMonth(actualMonth + 4)), true);
|
|
515
|
+
}
|
|
516
|
+
},
|
|
517
|
+
{
|
|
518
|
+
name: 'year',
|
|
519
|
+
format: 'yyyy',
|
|
520
|
+
split: 4,
|
|
521
|
+
steps: { year: 12 },
|
|
522
|
+
update: function (date, force) {
|
|
523
|
+
if (!this.built || force || parseInt(date.getFullYear() / 20, 10) !== parseInt(viewDate.year / 20, 10)) {
|
|
524
|
+
angular.extend(viewDate, {
|
|
525
|
+
year: picker.$date.getFullYear(),
|
|
526
|
+
month: picker.$date.getMonth(),
|
|
527
|
+
date: picker.$date.getDate()
|
|
528
|
+
});
|
|
529
|
+
picker.$build();
|
|
530
|
+
} else if (date.getFullYear() !== viewDate.year) {
|
|
531
|
+
angular.extend(viewDate, {
|
|
532
|
+
year: picker.$date.getFullYear(),
|
|
533
|
+
month: picker.$date.getMonth(),
|
|
534
|
+
date: picker.$date.getDate()
|
|
535
|
+
});
|
|
536
|
+
picker.$updateSelected();
|
|
537
|
+
}
|
|
538
|
+
},
|
|
539
|
+
build: function () {
|
|
540
|
+
var firstYear = viewDate.year - viewDate.year % (this.split * 3);
|
|
541
|
+
var years = [], year;
|
|
542
|
+
for (var i = 0; i < 12; i++) {
|
|
543
|
+
year = new Date(firstYear + i, 0, 1);
|
|
544
|
+
years.push({
|
|
545
|
+
date: year,
|
|
546
|
+
label: dateFilter(year, this.format),
|
|
547
|
+
selected: picker.$isSelected(year),
|
|
548
|
+
disabled: this.isDisabled(year)
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
scope.title = years[0].label + '-' + years[years.length - 1].label;
|
|
552
|
+
scope.labels = false;
|
|
553
|
+
scope.rows = split(years, this.split);
|
|
554
|
+
this.built = true;
|
|
555
|
+
},
|
|
556
|
+
isSelected: function (date) {
|
|
557
|
+
return picker.$date && date.getFullYear() === picker.$date.getFullYear();
|
|
558
|
+
},
|
|
559
|
+
isDisabled: function (date) {
|
|
560
|
+
var lastDate = +new Date(date.getFullYear() + 1, 0, 0);
|
|
561
|
+
return lastDate < options.minDate || date.getTime() > options.maxDate;
|
|
562
|
+
},
|
|
563
|
+
onKeyDown: function (evt) {
|
|
564
|
+
var actualYear = picker.$date.getFullYear();
|
|
565
|
+
if (evt.keyCode === 37)
|
|
566
|
+
picker.select(new Date(picker.$date.setYear(actualYear - 1)), true);
|
|
567
|
+
else if (evt.keyCode === 38)
|
|
568
|
+
picker.select(new Date(picker.$date.setYear(actualYear - 4)), true);
|
|
569
|
+
else if (evt.keyCode === 39)
|
|
570
|
+
picker.select(new Date(picker.$date.setYear(actualYear + 1)), true);
|
|
571
|
+
else if (evt.keyCode === 40)
|
|
572
|
+
picker.select(new Date(picker.$date.setYear(actualYear + 4)), true);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
];
|
|
576
|
+
return {
|
|
577
|
+
views: options.minView ? Array.prototype.slice.call(views, options.minView) : views,
|
|
578
|
+
viewDate: viewDate
|
|
579
|
+
};
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
];
|
|
583
|
+
});
|