ui-bootstrap-tpls.js 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274
  1. /*
  2. * angular-ui-bootstrap
  3. * http://angular-ui.github.io/bootstrap/
  4. * Version: 0.13.4 - 2015-09-03
  5. * License: MIT
  6. */
  7. angular.module("ui.bootstrap", ["ui.bootstrap.tpls","ui.bootstrap.position","ui.bootstrap.dateparser","ui.bootstrap.datepicker"]);
  8. angular.module("ui.bootstrap.tpls", ["template/datepicker/datepicker.html","template/datepicker/day.html","template/datepicker/month.html","template/datepicker/popup.html","template/datepicker/year.html"]);
  9. angular.module('ui.bootstrap.position', [])
  10. /**
  11. * A set of utility methods that can be use to retrieve position of DOM elements.
  12. * It is meant to be used where we need to absolute-position DOM elements in
  13. * relation to other, existing elements (this is the case for tooltips, popovers,
  14. * typeahead suggestions etc.).
  15. */
  16. .factory('$position', ['$document', '$window', function($document, $window) {
  17. function getStyle(el, cssprop) {
  18. if (el.currentStyle) { //IE
  19. return el.currentStyle[cssprop];
  20. } else if ($window.getComputedStyle) {
  21. return $window.getComputedStyle(el)[cssprop];
  22. }
  23. // finally try and get inline style
  24. return el.style[cssprop];
  25. }
  26. /**
  27. * Checks if a given element is statically positioned
  28. * @param element - raw DOM element
  29. */
  30. function isStaticPositioned(element) {
  31. return (getStyle(element, 'position') || 'static' ) === 'static';
  32. }
  33. /**
  34. * returns the closest, non-statically positioned parentOffset of a given element
  35. * @param element
  36. */
  37. var parentOffsetEl = function(element) {
  38. var docDomEl = $document[0];
  39. var offsetParent = element.offsetParent || docDomEl;
  40. while (offsetParent && offsetParent !== docDomEl && isStaticPositioned(offsetParent) ) {
  41. offsetParent = offsetParent.offsetParent;
  42. }
  43. return offsetParent || docDomEl;
  44. };
  45. return {
  46. /**
  47. * Provides read-only equivalent of jQuery's position function:
  48. * http://api.jquery.com/position/
  49. */
  50. position: function(element) {
  51. var elBCR = this.offset(element);
  52. var offsetParentBCR = { top: 0, left: 0 };
  53. var offsetParentEl = parentOffsetEl(element[0]);
  54. if (offsetParentEl != $document[0]) {
  55. offsetParentBCR = this.offset(angular.element(offsetParentEl));
  56. offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop;
  57. offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft;
  58. }
  59. var boundingClientRect = element[0].getBoundingClientRect();
  60. return {
  61. width: boundingClientRect.width || element.prop('offsetWidth'),
  62. height: boundingClientRect.height || element.prop('offsetHeight'),
  63. top: elBCR.top - offsetParentBCR.top,
  64. left: elBCR.left - offsetParentBCR.left
  65. };
  66. },
  67. /**
  68. * Provides read-only equivalent of jQuery's offset function:
  69. * http://api.jquery.com/offset/
  70. */
  71. offset: function(element) {
  72. var boundingClientRect = element[0].getBoundingClientRect();
  73. return {
  74. width: boundingClientRect.width || element.prop('offsetWidth'),
  75. height: boundingClientRect.height || element.prop('offsetHeight'),
  76. top: boundingClientRect.top + ($window.pageYOffset || $document[0].documentElement.scrollTop),
  77. left: boundingClientRect.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft)
  78. };
  79. },
  80. /**
  81. * Provides coordinates for the targetEl in relation to hostEl
  82. */
  83. positionElements: function(hostEl, targetEl, positionStr, appendToBody) {
  84. var positionStrParts = positionStr.split('-');
  85. var pos0 = positionStrParts[0], pos1 = positionStrParts[1] || 'center';
  86. var hostElPos,
  87. targetElWidth,
  88. targetElHeight,
  89. targetElPos;
  90. hostElPos = appendToBody ? this.offset(hostEl) : this.position(hostEl);
  91. targetElWidth = targetEl.prop('offsetWidth');
  92. targetElHeight = targetEl.prop('offsetHeight');
  93. var shiftWidth = {
  94. center: function() {
  95. return hostElPos.left + hostElPos.width / 2 - targetElWidth / 2;
  96. },
  97. left: function() {
  98. return hostElPos.left;
  99. },
  100. right: function() {
  101. return hostElPos.left + hostElPos.width;
  102. }
  103. };
  104. var shiftHeight = {
  105. center: function() {
  106. return hostElPos.top + hostElPos.height / 2 - targetElHeight / 2;
  107. },
  108. top: function() {
  109. return hostElPos.top;
  110. },
  111. bottom: function() {
  112. return hostElPos.top + hostElPos.height;
  113. }
  114. };
  115. switch (pos0) {
  116. case 'right':
  117. targetElPos = {
  118. top: shiftHeight[pos1](),
  119. left: shiftWidth[pos0]()
  120. };
  121. break;
  122. case 'left':
  123. targetElPos = {
  124. top: shiftHeight[pos1](),
  125. left: hostElPos.left - targetElWidth
  126. };
  127. break;
  128. case 'bottom':
  129. targetElPos = {
  130. top: shiftHeight[pos0](),
  131. left: shiftWidth[pos1]()
  132. };
  133. break;
  134. default:
  135. targetElPos = {
  136. top: hostElPos.top - targetElHeight,
  137. left: shiftWidth[pos1]()
  138. };
  139. break;
  140. }
  141. return targetElPos;
  142. }
  143. };
  144. }]);
  145. angular.module('ui.bootstrap.dateparser', [])
  146. .service('dateParser', ['$log', '$locale', 'orderByFilter', function($log, $locale, orderByFilter) {
  147. // Pulled from https://github.com/mbostock/d3/blob/master/src/format/requote.js
  148. var SPECIAL_CHARACTERS_REGEXP = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;
  149. this.parsers = {};
  150. var formatCodeToRegex = {
  151. 'yyyy': {
  152. regex: '\\d{4}',
  153. apply: function(value) { this.year = +value; }
  154. },
  155. 'yy': {
  156. regex: '\\d{2}',
  157. apply: function(value) { this.year = +value + 2000; }
  158. },
  159. 'y': {
  160. regex: '\\d{1,4}',
  161. apply: function(value) { this.year = +value; }
  162. },
  163. 'MMMM': {
  164. regex: $locale.DATETIME_FORMATS.MONTH.join('|'),
  165. apply: function(value) { this.month = $locale.DATETIME_FORMATS.MONTH.indexOf(value); }
  166. },
  167. 'MMM': {
  168. regex: $locale.DATETIME_FORMATS.SHORTMONTH.join('|'),
  169. apply: function(value) { this.month = $locale.DATETIME_FORMATS.SHORTMONTH.indexOf(value); }
  170. },
  171. 'MM': {
  172. regex: '0[1-9]|1[0-2]',
  173. apply: function(value) { this.month = value - 1; }
  174. },
  175. 'M': {
  176. regex: '[1-9]|1[0-2]',
  177. apply: function(value) { this.month = value - 1; }
  178. },
  179. 'dd': {
  180. regex: '[0-2][0-9]{1}|3[0-1]{1}',
  181. apply: function(value) { this.date = +value; }
  182. },
  183. 'd': {
  184. regex: '[1-2]?[0-9]{1}|3[0-1]{1}',
  185. apply: function(value) { this.date = +value; }
  186. },
  187. 'EEEE': {
  188. regex: $locale.DATETIME_FORMATS.DAY.join('|')
  189. },
  190. 'EEE': {
  191. regex: $locale.DATETIME_FORMATS.SHORTDAY.join('|')
  192. },
  193. 'HH': {
  194. regex: '(?:0|1)[0-9]|2[0-3]',
  195. apply: function(value) { this.hours = +value; }
  196. },
  197. 'hh': {
  198. regex: '0[0-9]|1[0-2]',
  199. apply: function(value) { this.hours = +value; }
  200. },
  201. 'H': {
  202. regex: '1?[0-9]|2[0-3]',
  203. apply: function(value) { this.hours = +value; }
  204. },
  205. 'h': {
  206. regex: '[0-9]|1[0-2]',
  207. apply: function(value) { this.hours = +value; }
  208. },
  209. 'mm': {
  210. regex: '[0-5][0-9]',
  211. apply: function(value) { this.minutes = +value; }
  212. },
  213. 'm': {
  214. regex: '[0-9]|[1-5][0-9]',
  215. apply: function(value) { this.minutes = +value; }
  216. },
  217. 'sss': {
  218. regex: '[0-9][0-9][0-9]',
  219. apply: function(value) { this.milliseconds = +value; }
  220. },
  221. 'ss': {
  222. regex: '[0-5][0-9]',
  223. apply: function(value) { this.seconds = +value; }
  224. },
  225. 's': {
  226. regex: '[0-9]|[1-5][0-9]',
  227. apply: function(value) { this.seconds = +value; }
  228. },
  229. 'a': {
  230. regex: $locale.DATETIME_FORMATS.AMPMS.join('|'),
  231. apply: function(value) {
  232. if (this.hours === 12) {
  233. this.hours = 0;
  234. }
  235. if (value === 'PM') {
  236. this.hours += 12;
  237. }
  238. }
  239. }
  240. };
  241. function createParser(format) {
  242. var map = [], regex = format.split('');
  243. angular.forEach(formatCodeToRegex, function(data, code) {
  244. var index = format.indexOf(code);
  245. if (index > -1) {
  246. format = format.split('');
  247. regex[index] = '(' + data.regex + ')';
  248. format[index] = '$'; // Custom symbol to define consumed part of format
  249. for (var i = index + 1, n = index + code.length; i < n; i++) {
  250. regex[i] = '';
  251. format[i] = '$';
  252. }
  253. format = format.join('');
  254. map.push({ index: index, apply: data.apply });
  255. }
  256. });
  257. return {
  258. regex: new RegExp('^' + regex.join('') + '$'),
  259. map: orderByFilter(map, 'index')
  260. };
  261. }
  262. this.parse = function(input, format, baseDate) {
  263. if (!angular.isString(input) || !format) {
  264. return input;
  265. }
  266. format = $locale.DATETIME_FORMATS[format] || format;
  267. format = format.replace(SPECIAL_CHARACTERS_REGEXP, '\\$&');
  268. if (!this.parsers[format]) {
  269. this.parsers[format] = createParser(format);
  270. }
  271. var parser = this.parsers[format],
  272. regex = parser.regex,
  273. map = parser.map,
  274. results = input.match(regex);
  275. if (results && results.length) {
  276. var fields, dt;
  277. if (angular.isDate(baseDate) && !isNaN(baseDate.getTime())) {
  278. fields = {
  279. year: baseDate.getFullYear(),
  280. month: baseDate.getMonth(),
  281. date: baseDate.getDate(),
  282. hours: baseDate.getHours(),
  283. minutes: baseDate.getMinutes(),
  284. seconds: baseDate.getSeconds(),
  285. milliseconds: baseDate.getMilliseconds()
  286. };
  287. } else {
  288. if (baseDate) {
  289. $log.warn('dateparser:', 'baseDate is not a valid date');
  290. }
  291. fields = { year: 1900, month: 0, date: 1, hours: 0, minutes: 0, seconds: 0, milliseconds: 0 };
  292. }
  293. for (var i = 1, n = results.length; i < n; i++) {
  294. var mapper = map[i-1];
  295. if (mapper.apply) {
  296. mapper.apply.call(fields, results[i]);
  297. }
  298. }
  299. if (isValid(fields.year, fields.month, fields.date)) {
  300. dt = new Date(fields.year, fields.month, fields.date,
  301. fields.hours, fields.minutes, fields.seconds,
  302. fields.milliseconds || 0);
  303. }
  304. return dt;
  305. }
  306. };
  307. // Check if date is valid for specific month (and year for February).
  308. // Month: 0 = Jan, 1 = Feb, etc
  309. function isValid(year, month, date) {
  310. if (date < 1) {
  311. return false;
  312. }
  313. if (month === 1 && date > 28) {
  314. return date === 29 && ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0);
  315. }
  316. if (month === 3 || month === 5 || month === 8 || month === 10) {
  317. return date < 31;
  318. }
  319. return true;
  320. }
  321. }]);
  322. angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootstrap.position'])
  323. .value('$datepickerSuppressError', false)
  324. .constant('datepickerConfig', {
  325. formatDay: 'dd',
  326. formatMonth: 'MMMM',
  327. formatYear: 'yyyy',
  328. formatDayHeader: 'EEE',
  329. formatDayTitle: 'MMMM yyyy',
  330. formatMonthTitle: 'yyyy',
  331. datepickerMode: 'day',
  332. minMode: 'day',
  333. maxMode: 'year',
  334. showWeeks: true,
  335. startingDay: 0,
  336. yearRange: 20,
  337. minDate: null,
  338. maxDate: null,
  339. shortcutPropagation: false
  340. })
  341. .controller('DatepickerController', ['$scope', '$attrs', '$parse', '$interpolate', '$log', 'dateFilter', 'datepickerConfig', '$datepickerSuppressError', function($scope, $attrs, $parse, $interpolate, $log, dateFilter, datepickerConfig, $datepickerSuppressError) {
  342. var self = this,
  343. ngModelCtrl = { $setViewValue: angular.noop }; // nullModelCtrl;
  344. // Modes chain
  345. this.modes = ['day', 'month', 'year'];
  346. // Configuration attributes
  347. angular.forEach(['formatDay', 'formatMonth', 'formatYear', 'formatDayHeader', 'formatDayTitle', 'formatMonthTitle',
  348. 'showWeeks', 'startingDay', 'yearRange', 'shortcutPropagation'], function(key, index) {
  349. self[key] = angular.isDefined($attrs[key]) ? (index < 6 ? $interpolate($attrs[key])($scope.$parent) : $scope.$parent.$eval($attrs[key])) : datepickerConfig[key];
  350. });
  351. // Watchable date attributes
  352. angular.forEach(['minDate', 'maxDate'], function(key) {
  353. if ($attrs[key]) {
  354. $scope.$parent.$watch($parse($attrs[key]), function(value) {
  355. self[key] = value ? new Date(value) : null;
  356. self.refreshView();
  357. });
  358. } else {
  359. self[key] = datepickerConfig[key] ? new Date(datepickerConfig[key]) : null;
  360. }
  361. });
  362. angular.forEach(['minMode', 'maxMode'], function(key) {
  363. if ($attrs[key]) {
  364. $scope.$parent.$watch($parse($attrs[key]), function(value) {
  365. self[key] = angular.isDefined(value) ? value : $attrs[key];
  366. $scope[key] = self[key];
  367. if ((key == 'minMode' && self.modes.indexOf($scope.datepickerMode) < self.modes.indexOf(self[key])) || (key == 'maxMode' && self.modes.indexOf($scope.datepickerMode) > self.modes.indexOf(self[key]))) {
  368. $scope.datepickerMode = self[key];
  369. }
  370. });
  371. } else {
  372. self[key] = datepickerConfig[key] || null;
  373. $scope[key] = self[key];
  374. }
  375. });
  376. $scope.datepickerMode = $scope.datepickerMode || datepickerConfig.datepickerMode;
  377. $scope.uniqueId = 'datepicker-' + $scope.$id + '-' + Math.floor(Math.random() * 10000);
  378. if (angular.isDefined($attrs.initDate)) {
  379. this.activeDate = $scope.$parent.$eval($attrs.initDate) || new Date();
  380. $scope.$parent.$watch($attrs.initDate, function(initDate) {
  381. if (initDate && (ngModelCtrl.$isEmpty(ngModelCtrl.$modelValue) || ngModelCtrl.$invalid)) {
  382. self.activeDate = initDate;
  383. self.refreshView();
  384. }
  385. });
  386. } else {
  387. this.activeDate = new Date();
  388. }
  389. $scope.isActive = function(dateObject) {
  390. if (self.compare(dateObject.date, self.activeDate) === 0) {
  391. $scope.activeDateId = dateObject.uid;
  392. return true;
  393. }
  394. return false;
  395. };
  396. this.init = function(ngModelCtrl_) {
  397. ngModelCtrl = ngModelCtrl_;
  398. ngModelCtrl.$render = function() {
  399. self.render();
  400. };
  401. };
  402. this.render = function() {
  403. if (ngModelCtrl.$viewValue) {
  404. var date = new Date(ngModelCtrl.$viewValue),
  405. isValid = !isNaN(date);
  406. if (isValid) {
  407. this.activeDate = date;
  408. } else if (!$datepickerSuppressError) {
  409. $log.error('Datepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.');
  410. }
  411. }
  412. this.refreshView();
  413. };
  414. this.refreshView = function() {
  415. if (this.element) {
  416. this._refreshView();
  417. var date = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null;
  418. ngModelCtrl.$setValidity('dateDisabled', !date || (this.element && !this.isDisabled(date)));
  419. }
  420. };
  421. this.createDateObject = function(date, format) {
  422. var model = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null;
  423. return {
  424. date: date,
  425. label: dateFilter(date, format),
  426. selected: model && this.compare(date, model) === 0,
  427. disabled: this.isDisabled(date),
  428. current: this.compare(date, new Date()) === 0,
  429. customClass: this.customClass(date)
  430. };
  431. };
  432. this.isDisabled = function(date) {
  433. return ((this.minDate && this.compare(date, this.minDate) < 0) || (this.maxDate && this.compare(date, this.maxDate) > 0) || ($attrs.dateDisabled && $scope.dateDisabled({date: date, mode: $scope.datepickerMode})));
  434. };
  435. this.customClass = function(date) {
  436. return $scope.customClass({date: date, mode: $scope.datepickerMode});
  437. };
  438. // Split array into smaller arrays
  439. this.split = function(arr, size) {
  440. var arrays = [];
  441. while (arr.length > 0) {
  442. arrays.push(arr.splice(0, size));
  443. }
  444. return arrays;
  445. };
  446. // Fix a hard-reprodusible bug with timezones
  447. // The bug depends on OS, browser, current timezone and current date
  448. // i.e.
  449. // var date = new Date(2014, 0, 1);
  450. // console.log(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours());
  451. // can result in "2013 11 31 23" because of the bug.
  452. this.fixTimeZone = function(date) {
  453. var hours = date.getHours();
  454. date.setHours(hours === 23 ? hours + 2 : 0);
  455. };
  456. $scope.select = function(date) {
  457. if ($scope.datepickerMode === self.minMode) {
  458. var dt = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : new Date(0, 0, 0, 0, 0, 0, 0);
  459. dt.setFullYear(date.getFullYear(), date.getMonth(), date.getDate());
  460. ngModelCtrl.$setViewValue(dt);
  461. ngModelCtrl.$render();
  462. } else {
  463. self.activeDate = date;
  464. $scope.datepickerMode = self.modes[self.modes.indexOf($scope.datepickerMode) - 1];
  465. }
  466. };
  467. $scope.move = function(direction) {
  468. var year = self.activeDate.getFullYear() + direction * (self.step.years || 0),
  469. month = self.activeDate.getMonth() + direction * (self.step.months || 0);
  470. self.activeDate.setFullYear(year, month, 1);
  471. self.refreshView();
  472. };
  473. $scope.toggleMode = function(direction) {
  474. direction = direction || 1;
  475. if (($scope.datepickerMode === self.maxMode && direction === 1) || ($scope.datepickerMode === self.minMode && direction === -1)) {
  476. return;
  477. }
  478. $scope.datepickerMode = self.modes[self.modes.indexOf($scope.datepickerMode) + direction];
  479. };
  480. // Key event mapper
  481. $scope.keys = { 13: 'enter', 32: 'space', 33: 'pageup', 34: 'pagedown', 35: 'end', 36: 'home', 37: 'left', 38: 'up', 39: 'right', 40: 'down' };
  482. var focusElement = function() {
  483. self.element[0].focus();
  484. };
  485. // Listen for focus requests from popup directive
  486. $scope.$on('datepicker.focus', focusElement);
  487. $scope.keydown = function(evt) {
  488. var key = $scope.keys[evt.which];
  489. if (!key || evt.shiftKey || evt.altKey) {
  490. return;
  491. }
  492. evt.preventDefault();
  493. if (!self.shortcutPropagation) {
  494. evt.stopPropagation();
  495. }
  496. if (key === 'enter' || key === 'space') {
  497. if (self.isDisabled(self.activeDate)) {
  498. return; // do nothing
  499. }
  500. $scope.select(self.activeDate);
  501. focusElement();
  502. } else if (evt.ctrlKey && (key === 'up' || key === 'down')) {
  503. $scope.toggleMode(key === 'up' ? 1 : -1);
  504. focusElement();
  505. } else {
  506. self.handleKeyDown(key, evt);
  507. self.refreshView();
  508. }
  509. };
  510. }])
  511. .directive('datepicker', function() {
  512. return {
  513. restrict: 'EA',
  514. replace: true,
  515. templateUrl: function(element, attrs) {
  516. return attrs.templateUrl || 'template/datepicker/datepicker.html';
  517. },
  518. scope: {
  519. datepickerMode: '=?',
  520. dateDisabled: '&',
  521. customClass: '&',
  522. shortcutPropagation: '&?'
  523. },
  524. require: ['datepicker', '^ngModel'],
  525. controller: 'DatepickerController',
  526. controllerAs: 'datepicker',
  527. link: function(scope, element, attrs, ctrls) {
  528. var datepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1];
  529. datepickerCtrl.init(ngModelCtrl);
  530. }
  531. };
  532. })
  533. .directive('daypicker', ['dateFilter', function(dateFilter) {
  534. return {
  535. restrict: 'EA',
  536. replace: true,
  537. templateUrl: 'template/datepicker/day.html',
  538. require: '^datepicker',
  539. link: function(scope, element, attrs, ctrl) {
  540. scope.showWeeks = ctrl.showWeeks;
  541. ctrl.step = { months: 1 };
  542. ctrl.element = element;
  543. var DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
  544. function getDaysInMonth(year, month) {
  545. return ((month === 1) && (year % 4 === 0) && ((year % 100 !== 0) || (year % 400 === 0))) ? 29 : DAYS_IN_MONTH[month];
  546. }
  547. function getDates(startDate, n) {
  548. var dates = new Array(n), current = new Date(startDate), i = 0, date;
  549. while (i < n) {
  550. date = new Date(current);
  551. ctrl.fixTimeZone(date);
  552. dates[i++] = date;
  553. current.setDate(current.getDate() + 1);
  554. }
  555. return dates;
  556. }
  557. ctrl._refreshView = function() {
  558. var year = ctrl.activeDate.getFullYear(),
  559. month = ctrl.activeDate.getMonth(),
  560. firstDayOfMonth = new Date(year, month, 1),
  561. difference = ctrl.startingDay - firstDayOfMonth.getDay(),
  562. numDisplayedFromPreviousMonth = (difference > 0) ? 7 - difference : - difference,
  563. firstDate = new Date(firstDayOfMonth);
  564. if (numDisplayedFromPreviousMonth > 0) {
  565. firstDate.setDate(-numDisplayedFromPreviousMonth + 1);
  566. }
  567. // 42 is the number of days on a six-month calendar
  568. var days = getDates(firstDate, 42);
  569. for (var i = 0; i < 42; i ++) {
  570. days[i] = angular.extend(ctrl.createDateObject(days[i], ctrl.formatDay), {
  571. secondary: days[i].getMonth() !== month,
  572. uid: scope.uniqueId + '-' + i
  573. });
  574. }
  575. scope.labels = new Array(7);
  576. for (var j = 0; j < 7; j++) {
  577. scope.labels[j] = {
  578. abbr: dateFilter(days[j].date, ctrl.formatDayHeader),
  579. full: dateFilter(days[j].date, 'EEEE')
  580. };
  581. }
  582. scope.title = dateFilter(ctrl.activeDate, ctrl.formatDayTitle);
  583. scope.rows = ctrl.split(days, 7);
  584. if (scope.showWeeks) {
  585. scope.weekNumbers = [];
  586. var thursdayIndex = (4 + 7 - ctrl.startingDay) % 7,
  587. numWeeks = scope.rows.length;
  588. for (var curWeek = 0; curWeek < numWeeks; curWeek++) {
  589. scope.weekNumbers.push(
  590. getISO8601WeekNumber(scope.rows[curWeek][thursdayIndex].date));
  591. }
  592. }
  593. };
  594. ctrl.compare = function(date1, date2) {
  595. return (new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()) - new Date(date2.getFullYear(), date2.getMonth(), date2.getDate()));
  596. };
  597. function getISO8601WeekNumber(date) {
  598. var checkDate = new Date(date);
  599. checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7)); // Thursday
  600. var time = checkDate.getTime();
  601. checkDate.setMonth(0); // Compare with Jan 1
  602. checkDate.setDate(1);
  603. return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
  604. }
  605. ctrl.handleKeyDown = function(key, evt) {
  606. var date = ctrl.activeDate.getDate();
  607. if (key === 'left') {
  608. date = date - 1; // up
  609. } else if (key === 'up') {
  610. date = date - 7; // down
  611. } else if (key === 'right') {
  612. date = date + 1; // down
  613. } else if (key === 'down') {
  614. date = date + 7;
  615. } else if (key === 'pageup' || key === 'pagedown') {
  616. var month = ctrl.activeDate.getMonth() + (key === 'pageup' ? - 1 : 1);
  617. ctrl.activeDate.setMonth(month, 1);
  618. date = Math.min(getDaysInMonth(ctrl.activeDate.getFullYear(), ctrl.activeDate.getMonth()), date);
  619. } else if (key === 'home') {
  620. date = 1;
  621. } else if (key === 'end') {
  622. date = getDaysInMonth(ctrl.activeDate.getFullYear(), ctrl.activeDate.getMonth());
  623. }
  624. ctrl.activeDate.setDate(date);
  625. };
  626. ctrl.refreshView();
  627. }
  628. };
  629. }])
  630. .directive('monthpicker', ['dateFilter', function(dateFilter) {
  631. return {
  632. restrict: 'EA',
  633. replace: true,
  634. templateUrl: 'template/datepicker/month.html',
  635. require: '^datepicker',
  636. link: function(scope, element, attrs, ctrl) {
  637. ctrl.step = { years: 1 };
  638. ctrl.element = element;
  639. ctrl._refreshView = function() {
  640. var months = new Array(12),
  641. year = ctrl.activeDate.getFullYear(),
  642. date;
  643. for (var i = 0; i < 12; i++) {
  644. date = new Date(year, i, 1);
  645. ctrl.fixTimeZone(date);
  646. months[i] = angular.extend(ctrl.createDateObject(date, ctrl.formatMonth), {
  647. uid: scope.uniqueId + '-' + i
  648. });
  649. }
  650. scope.title = dateFilter(ctrl.activeDate, ctrl.formatMonthTitle);
  651. scope.rows = ctrl.split(months, 3);
  652. };
  653. ctrl.compare = function(date1, date2) {
  654. return new Date(date1.getFullYear(), date1.getMonth()) - new Date(date2.getFullYear(), date2.getMonth());
  655. };
  656. ctrl.handleKeyDown = function(key, evt) {
  657. var date = ctrl.activeDate.getMonth();
  658. if (key === 'left') {
  659. date = date - 1; // up
  660. } else if (key === 'up') {
  661. date = date - 3; // down
  662. } else if (key === 'right') {
  663. date = date + 1; // down
  664. } else if (key === 'down') {
  665. date = date + 3;
  666. } else if (key === 'pageup' || key === 'pagedown') {
  667. var year = ctrl.activeDate.getFullYear() + (key === 'pageup' ? - 1 : 1);
  668. ctrl.activeDate.setFullYear(year);
  669. } else if (key === 'home') {
  670. date = 0;
  671. } else if (key === 'end') {
  672. date = 11;
  673. }
  674. ctrl.activeDate.setMonth(date);
  675. };
  676. ctrl.refreshView();
  677. }
  678. };
  679. }])
  680. .directive('yearpicker', ['dateFilter', function(dateFilter) {
  681. return {
  682. restrict: 'EA',
  683. replace: true,
  684. templateUrl: 'template/datepicker/year.html',
  685. require: '^datepicker',
  686. link: function(scope, element, attrs, ctrl) {
  687. var range = ctrl.yearRange;
  688. ctrl.step = { years: range };
  689. ctrl.element = element;
  690. function getStartingYear( year ) {
  691. return parseInt((year - 1) / range, 10) * range + 1;
  692. }
  693. ctrl._refreshView = function() {
  694. var years = new Array(range), date;
  695. for (var i = 0, start = getStartingYear(ctrl.activeDate.getFullYear()); i < range; i++) {
  696. date = new Date(start + i, 0, 1);
  697. ctrl.fixTimeZone(date);
  698. years[i] = angular.extend(ctrl.createDateObject(date, ctrl.formatYear), {
  699. uid: scope.uniqueId + '-' + i
  700. });
  701. }
  702. scope.title = [years[0].label, years[range - 1].label].join(' - ');
  703. scope.rows = ctrl.split(years, 5);
  704. };
  705. ctrl.compare = function(date1, date2) {
  706. return date1.getFullYear() - date2.getFullYear();
  707. };
  708. ctrl.handleKeyDown = function(key, evt) {
  709. var date = ctrl.activeDate.getFullYear();
  710. if (key === 'left') {
  711. date = date - 1; // up
  712. } else if (key === 'up') {
  713. date = date - 5; // down
  714. } else if (key === 'right') {
  715. date = date + 1; // down
  716. } else if (key === 'down') {
  717. date = date + 5;
  718. } else if (key === 'pageup' || key === 'pagedown') {
  719. date += (key === 'pageup' ? - 1 : 1) * ctrl.step.years;
  720. } else if (key === 'home') {
  721. date = getStartingYear(ctrl.activeDate.getFullYear());
  722. } else if (key === 'end') {
  723. date = getStartingYear(ctrl.activeDate.getFullYear()) + range - 1;
  724. }
  725. ctrl.activeDate.setFullYear(date);
  726. };
  727. ctrl.refreshView();
  728. }
  729. };
  730. }])
  731. .constant('datepickerPopupConfig', {
  732. datepickerPopup: 'yyyy-MM-dd',
  733. datepickerPopupTemplateUrl: 'template/datepicker/popup.html',
  734. datepickerTemplateUrl: 'template/datepicker/datepicker.html',
  735. html5Types: {
  736. date: 'yyyy-MM-dd',
  737. 'datetime-local': 'yyyy-MM-ddTHH:mm:ss.sss',
  738. 'month': 'yyyy-MM'
  739. },
  740. currentText: 'Today',
  741. clearText: 'Clear',
  742. closeText: 'Done',
  743. closeOnDateSelection: true,
  744. appendToBody: false,
  745. showButtonBar: true,
  746. onOpenFocus: true
  747. })
  748. .directive('datepickerPopup', ['$compile', '$parse', '$document', '$rootScope', '$position', 'dateFilter', 'dateParser', 'datepickerPopupConfig', '$timeout',
  749. function($compile, $parse, $document, $rootScope, $position, dateFilter, dateParser, datepickerPopupConfig, $timeout) {
  750. return {
  751. restrict: 'EA',
  752. require: 'ngModel',
  753. scope: {
  754. isOpen: '=?',
  755. currentText: '@',
  756. clearText: '@',
  757. closeText: '@',
  758. dateDisabled: '&',
  759. customClass: '&'
  760. },
  761. link: function(scope, element, attrs, ngModel) {
  762. var dateFormat,
  763. closeOnDateSelection = angular.isDefined(attrs.closeOnDateSelection) ? scope.$parent.$eval(attrs.closeOnDateSelection) : datepickerPopupConfig.closeOnDateSelection,
  764. appendToBody = angular.isDefined(attrs.datepickerAppendToBody) ? scope.$parent.$eval(attrs.datepickerAppendToBody) : datepickerPopupConfig.appendToBody,
  765. onOpenFocus = angular.isDefined(attrs.onOpenFocus) ? scope.$parent.$eval(attrs.onOpenFocus) : datepickerPopupConfig.onOpenFocus,
  766. datepickerPopupTemplateUrl = angular.isDefined(attrs.datepickerPopupTemplateUrl) ? attrs.datepickerPopupTemplateUrl : datepickerPopupConfig.datepickerPopupTemplateUrl,
  767. datepickerTemplateUrl = angular.isDefined(attrs.datepickerTemplateUrl) ? attrs.datepickerTemplateUrl : datepickerPopupConfig.datepickerTemplateUrl,
  768. cache = {};
  769. scope.showButtonBar = angular.isDefined(attrs.showButtonBar) ? scope.$parent.$eval(attrs.showButtonBar) : datepickerPopupConfig.showButtonBar;
  770. scope.getText = function(key) {
  771. return scope[key + 'Text'] || datepickerPopupConfig[key + 'Text'];
  772. };
  773. scope.isDisabled = function(date) {
  774. if (date === 'today') {
  775. date = new Date();
  776. }
  777. return ((scope.watchData.minDate && scope.compare(date, cache.minDate) < 0) ||
  778. (scope.watchData.maxDate && scope.compare(date, cache.maxDate) > 0));
  779. };
  780. scope.compare = function(date1, date2) {
  781. return (new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()) - new Date(date2.getFullYear(), date2.getMonth(), date2.getDate()));
  782. };
  783. var isHtml5DateInput = false;
  784. if (datepickerPopupConfig.html5Types[attrs.type]) {
  785. dateFormat = datepickerPopupConfig.html5Types[attrs.type];
  786. isHtml5DateInput = true;
  787. } else {
  788. dateFormat = attrs.datepickerPopup || datepickerPopupConfig.datepickerPopup;
  789. attrs.$observe('datepickerPopup', function(value, oldValue) {
  790. var newDateFormat = value || datepickerPopupConfig.datepickerPopup;
  791. // Invalidate the $modelValue to ensure that formatters re-run
  792. // FIXME: Refactor when PR is merged: https://github.com/angular/angular.js/pull/10764
  793. if (newDateFormat !== dateFormat) {
  794. dateFormat = newDateFormat;
  795. ngModel.$modelValue = null;
  796. if (!dateFormat) {
  797. throw new Error('datepickerPopup must have a date format specified.');
  798. }
  799. }
  800. });
  801. }
  802. if (!dateFormat) {
  803. throw new Error('datepickerPopup must have a date format specified.');
  804. }
  805. if (isHtml5DateInput && attrs.datepickerPopup) {
  806. throw new Error('HTML5 date input types do not support custom formats.');
  807. }
  808. // popup element used to display calendar
  809. var popupEl = angular.element('<div datepicker-popup-wrap><div datepicker></div></div>');
  810. popupEl.attr({
  811. 'ng-model': 'date',
  812. 'ng-change': 'dateSelection(date)',
  813. 'template-url': datepickerPopupTemplateUrl
  814. });
  815. function cameltoDash(string) {
  816. return string.replace(/([A-Z])/g, function($1) { return '-' + $1.toLowerCase(); });
  817. }
  818. // datepicker element
  819. var datepickerEl = angular.element(popupEl.children()[0]);
  820. datepickerEl.attr('template-url', datepickerTemplateUrl);
  821. if (isHtml5DateInput) {
  822. if (attrs.type === 'month') {
  823. datepickerEl.attr('datepicker-mode', '"month"');
  824. datepickerEl.attr('min-mode', 'month');
  825. }
  826. }
  827. if (attrs.datepickerOptions) {
  828. var options = scope.$parent.$eval(attrs.datepickerOptions);
  829. if (options && options.initDate) {
  830. scope.initDate = options.initDate;
  831. datepickerEl.attr('init-date', 'initDate');
  832. delete options.initDate;
  833. }
  834. angular.forEach(options, function(value, option) {
  835. datepickerEl.attr( cameltoDash(option), value );
  836. });
  837. }
  838. scope.watchData = {};
  839. angular.forEach(['minMode', 'maxMode', 'minDate', 'maxDate', 'datepickerMode', 'initDate', 'shortcutPropagation'], function(key) {
  840. if (attrs[key]) {
  841. var getAttribute = $parse(attrs[key]);
  842. scope.$parent.$watch(getAttribute, function(value) {
  843. scope.watchData[key] = value;
  844. if (key === 'minDate' || key === 'maxDate') {
  845. cache[key] = new Date(value);
  846. }
  847. });
  848. datepickerEl.attr(cameltoDash(key), 'watchData.' + key);
  849. // Propagate changes from datepicker to outside
  850. if (key === 'datepickerMode') {
  851. var setAttribute = getAttribute.assign;
  852. scope.$watch('watchData.' + key, function(value, oldvalue) {
  853. if (angular.isFunction(setAttribute) && value !== oldvalue) {
  854. setAttribute(scope.$parent, value);
  855. }
  856. });
  857. }
  858. }
  859. });
  860. if (attrs.dateDisabled) {
  861. datepickerEl.attr('date-disabled', 'dateDisabled({ date: date, mode: mode })');
  862. }
  863. if (attrs.showWeeks) {
  864. datepickerEl.attr('show-weeks', attrs.showWeeks);
  865. }
  866. if (attrs.customClass) {
  867. datepickerEl.attr('custom-class', 'customClass({ date: date, mode: mode })');
  868. }
  869. function parseDate(viewValue) {
  870. if (angular.isNumber(viewValue)) {
  871. // presumably timestamp to date object
  872. viewValue = new Date(viewValue);
  873. }
  874. if (!viewValue) {
  875. return null;
  876. } else if (angular.isDate(viewValue) && !isNaN(viewValue)) {
  877. return viewValue;
  878. } else if (angular.isString(viewValue)) {
  879. var date = dateParser.parse(viewValue, dateFormat, scope.date);
  880. if (isNaN(date)) {
  881. return undefined;
  882. } else {
  883. return date;
  884. }
  885. } else {
  886. return undefined;
  887. }
  888. }
  889. function validator(modelValue, viewValue) {
  890. var value = modelValue || viewValue;
  891. if (!attrs.ngRequired && !value) {
  892. return true;
  893. }
  894. if (angular.isNumber(value)) {
  895. value = new Date(value);
  896. }
  897. if (!value) {
  898. return true;
  899. } else if (angular.isDate(value) && !isNaN(value)) {
  900. return true;
  901. } else if (angular.isString(value)) {
  902. var date = dateParser.parse(value, dateFormat);
  903. return !isNaN(date);
  904. } else {
  905. return false;
  906. }
  907. }
  908. if (!isHtml5DateInput) {
  909. // Internal API to maintain the correct ng-invalid-[key] class
  910. ngModel.$$parserName = 'date';
  911. ngModel.$validators.date = validator;
  912. ngModel.$parsers.unshift(parseDate);
  913. ngModel.$formatters.push(function(value) {
  914. scope.date = value;
  915. return ngModel.$isEmpty(value) ? value : dateFilter(value, dateFormat);
  916. });
  917. } else {
  918. ngModel.$formatters.push(function(value) {
  919. scope.date = value;
  920. return value;
  921. });
  922. }
  923. // Inner change
  924. scope.dateSelection = function(dt) {
  925. if (angular.isDefined(dt)) {
  926. scope.date = dt;
  927. }
  928. var date = scope.date ? dateFilter(scope.date, dateFormat) : null; // Setting to NULL is necessary for form validators to function
  929. element.val(date);
  930. ngModel.$setViewValue(date);
  931. if (closeOnDateSelection) {
  932. scope.isOpen = false;
  933. element[0].focus();
  934. }
  935. };
  936. // Detect changes in the view from the text box
  937. ngModel.$viewChangeListeners.push(function() {
  938. scope.date = dateParser.parse(ngModel.$viewValue, dateFormat, scope.date);
  939. });
  940. var documentClickBind = function(event) {
  941. if (scope.isOpen && !(element[0].contains(event.target) || popupEl[0].contains(event.target))) {
  942. scope.$apply(function() {
  943. scope.isOpen = false;
  944. });
  945. }
  946. };
  947. var inputKeydownBind = function(evt) {
  948. if (evt.which === 27 && scope.isOpen) {
  949. evt.preventDefault();
  950. evt.stopPropagation();
  951. scope.$apply(function() {
  952. scope.isOpen = false;
  953. });
  954. element[0].focus();
  955. } else if (evt.which === 40 && !scope.isOpen) {
  956. evt.preventDefault();
  957. evt.stopPropagation();
  958. scope.$apply(function() {
  959. scope.isOpen = true;
  960. });
  961. }
  962. };
  963. element.bind('keydown', inputKeydownBind);
  964. scope.keydown = function(evt) {
  965. if (evt.which === 27) {
  966. scope.isOpen = false;
  967. element[0].focus();
  968. }
  969. };
  970. scope.$watch('isOpen', function(value) {
  971. if (value) {
  972. scope.position = appendToBody ? $position.offset(element) : $position.position(element);
  973. scope.position.top = scope.position.top + element.prop('offsetHeight');
  974. $timeout(function() {
  975. if (onOpenFocus) {
  976. scope.$broadcast('datepicker.focus');
  977. }
  978. $document.bind('click', documentClickBind);
  979. }, 0, false);
  980. } else {
  981. $document.unbind('click', documentClickBind);
  982. }
  983. });
  984. scope.select = function(date) {
  985. if (date === 'today') {
  986. var today = new Date();
  987. if (angular.isDate(scope.date)) {
  988. date = new Date(scope.date);
  989. date.setFullYear(today.getFullYear(), today.getMonth(), today.getDate());
  990. } else {
  991. date = new Date(today.setHours(0, 0, 0, 0));
  992. }
  993. }
  994. scope.dateSelection(date);
  995. };
  996. scope.close = function() {
  997. scope.isOpen = false;
  998. element[0].focus();
  999. };
  1000. var $popup = $compile(popupEl)(scope);
  1001. // Prevent jQuery cache memory leak (template is now redundant after linking)
  1002. popupEl.remove();
  1003. if (appendToBody) {
  1004. $document.find('body').append($popup);
  1005. } else {
  1006. element.after($popup);
  1007. }
  1008. scope.$on('$destroy', function() {
  1009. if (scope.isOpen === true) {
  1010. if (!$rootScope.$$phase) {
  1011. scope.$apply(function() {
  1012. scope.isOpen = false;
  1013. });
  1014. }
  1015. }
  1016. $popup.remove();
  1017. element.unbind('keydown', inputKeydownBind);
  1018. $document.unbind('click', documentClickBind);
  1019. });
  1020. }
  1021. };
  1022. }])
  1023. .directive('datepickerPopupWrap', function() {
  1024. return {
  1025. restrict:'EA',
  1026. replace: true,
  1027. transclude: true,
  1028. templateUrl: function(element, attrs) {
  1029. return attrs.templateUrl || 'template/datepicker/popup.html';
  1030. }
  1031. };
  1032. });
  1033. angular.module("template/datepicker/datepicker.html", []).run(["$templateCache", function($templateCache) {
  1034. $templateCache.put("template/datepicker/datepicker.html",
  1035. "<div ng-switch=\"datepickerMode\" role=\"application\" ng-keydown=\"keydown($event)\">\n" +
  1036. " <daypicker ng-switch-when=\"day\" tabindex=\"0\"></daypicker>\n" +
  1037. " <monthpicker ng-switch-when=\"month\" tabindex=\"0\"></monthpicker>\n" +
  1038. " <yearpicker ng-switch-when=\"year\" tabindex=\"0\"></yearpicker>\n" +
  1039. "</div>");
  1040. }]);
  1041. angular.module("template/datepicker/day.html", []).run(["$templateCache", function($templateCache) {
  1042. $templateCache.put("template/datepicker/day.html",
  1043. "<table role=\"grid\" aria-labelledby=\"{{::uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" +
  1044. " <thead>\n" +
  1045. " <tr>\n" +
  1046. " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\n" +
  1047. " <th colspan=\"{{::5 + showWeeks}}\"><button id=\"{{::uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm\" ng-click=\"toggleMode()\" ng-disabled=\"datepickerMode === maxMode\" tabindex=\"-1\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\n" +
  1048. " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\n" +
  1049. " </tr>\n" +
  1050. " <tr>\n" +
  1051. " <th ng-if=\"showWeeks\" class=\"text-center\"></th>\n" +
  1052. " <th ng-repeat=\"label in ::labels track by $index\" class=\"text-center\"><small aria-label=\"{{::label.full}}\">{{::label.abbr}}</small></th>\n" +
  1053. " </tr>\n" +
  1054. " </thead>\n" +
  1055. " <tbody>\n" +
  1056. " <tr ng-repeat=\"row in rows track by $index\">\n" +
  1057. " <td ng-if=\"showWeeks\" class=\"text-center h6\"><em>{{ weekNumbers[$index] }}</em></td>\n" +
  1058. " <td ng-repeat=\"dt in row track by dt.date\" class=\"text-center\" role=\"gridcell\" id=\"{{::dt.uid}}\" ng-class=\"::dt.customClass\">\n" +
  1059. " <button type=\"button\" style=\"min-width:100%;\" class=\"btn btn-default btn-sm\" ng-class=\"{'btn-info': dt.selected, active: isActive(dt)}\" ng-click=\"select(dt.date)\" ng-disabled=\"dt.disabled\" tabindex=\"-1\"><span ng-class=\"::{'text-muted': dt.secondary, 'text-info': dt.current}\">{{::dt.label}}</span></button>\n" +
  1060. " </td>\n" +
  1061. " </tr>\n" +
  1062. " </tbody>\n" +
  1063. "</table>\n" +
  1064. "");
  1065. }]);
  1066. angular.module("template/datepicker/month.html", []).run(["$templateCache", function($templateCache) {
  1067. $templateCache.put("template/datepicker/month.html",
  1068. "<table role=\"grid\" aria-labelledby=\"{{::uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" +
  1069. " <thead>\n" +
  1070. " <tr>\n" +
  1071. " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\n" +
  1072. " <th><button id=\"{{::uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm\" ng-click=\"toggleMode()\" ng-disabled=\"datepickerMode === maxMode\" tabindex=\"-1\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\n" +
  1073. " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\n" +
  1074. " </tr>\n" +
  1075. " </thead>\n" +
  1076. " <tbody>\n" +
  1077. " <tr ng-repeat=\"row in rows track by $index\">\n" +
  1078. " <td ng-repeat=\"dt in row track by dt.date\" class=\"text-center\" role=\"gridcell\" id=\"{{::dt.uid}}\" ng-class=\"::dt.customClass\">\n" +
  1079. " <button type=\"button\" style=\"min-width:100%;\" class=\"btn btn-default\" ng-class=\"{'btn-info': dt.selected, active: isActive(dt)}\" ng-click=\"select(dt.date)\" ng-disabled=\"dt.disabled\" tabindex=\"-1\"><span ng-class=\"::{'text-info': dt.current}\">{{::dt.label}}</span></button>\n" +
  1080. " </td>\n" +
  1081. " </tr>\n" +
  1082. " </tbody>\n" +
  1083. "</table>\n" +
  1084. "");
  1085. }]);
  1086. angular.module("template/datepicker/popup.html", []).run(["$templateCache", function($templateCache) {
  1087. $templateCache.put("template/datepicker/popup.html",
  1088. "<ul class=\"dropdown-menu\" ng-if=\"isOpen\" style=\"display: block\" ng-style=\"{top: position.top+'px', left: position.left+'px'}\" ng-keydown=\"keydown($event)\" ng-click=\"$event.stopPropagation()\">\n" +
  1089. " <li ng-transclude></li>\n" +
  1090. " <li ng-if=\"showButtonBar\" style=\"padding:10px 9px 2px\">\n" +
  1091. " <span class=\"btn-group pull-left\">\n" +
  1092. " <button type=\"button\" class=\"btn btn-sm btn-info\" ng-click=\"select('today')\" ng-disabled=\"isDisabled('today')\">{{ getText('current') }}</button>\n" +
  1093. " <button type=\"button\" class=\"btn btn-sm btn-danger\" ng-click=\"select(null)\">{{ getText('clear') }}</button>\n" +
  1094. " </span>\n" +
  1095. " <button type=\"button\" class=\"btn btn-sm btn-primary pull-right\" ng-click=\"close()\">{{ getText('close') }}</button>\n" +
  1096. " </li>\n" +
  1097. "</ul>\n" +
  1098. "");
  1099. }]);
  1100. angular.module("template/datepicker/year.html", []).run(["$templateCache", function($templateCache) {
  1101. $templateCache.put("template/datepicker/year.html",
  1102. "<table role=\"grid\" aria-labelledby=\"{{::uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" +
  1103. " <thead>\n" +
  1104. " <tr>\n" +
  1105. " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\n" +
  1106. " <th colspan=\"3\"><button id=\"{{::uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm\" ng-click=\"toggleMode()\" ng-disabled=\"datepickerMode === maxMode\" tabindex=\"-1\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\n" +
  1107. " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\n" +
  1108. " </tr>\n" +
  1109. " </thead>\n" +
  1110. " <tbody>\n" +
  1111. " <tr ng-repeat=\"row in rows track by $index\">\n" +
  1112. " <td ng-repeat=\"dt in row track by dt.date\" class=\"text-center\" role=\"gridcell\" id=\"{{::dt.uid}}\">\n" +
  1113. " <button type=\"button\" style=\"min-width:100%;\" class=\"btn btn-default\" ng-class=\"{'btn-info': dt.selected, active: isActive(dt)}\" ng-click=\"select(dt.date)\" ng-disabled=\"dt.disabled\" tabindex=\"-1\"><span ng-class=\"::{'text-info': dt.current}\">{{::dt.label}}</span></button>\n" +
  1114. " </td>\n" +
  1115. " </tr>\n" +
  1116. " </tbody>\n" +
  1117. "</table>\n" +
  1118. "");
  1119. }]);