| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476 |
- /*
- * angular-ui-bootstrap
- * http://angular-ui.github.io/bootstrap/
- * Version: 0.13.4 - 2015-09-03
- * License: MIT
- */
- angular.module("ui.bootstrap", ["ui.bootstrap.position","ui.bootstrap.dateparser","ui.bootstrap.datepicker","ui.bootstrap.tabs"]);
- angular.module('ui.bootstrap.position', [])
- /**
- * A set of utility methods that can be use to retrieve position of DOM elements.
- * It is meant to be used where we need to absolute-position DOM elements in
- * relation to other, existing elements (this is the case for tooltips, popovers,
- * typeahead suggestions etc.).
- */
- .factory('$position', ['$document', '$window', function($document, $window) {
- function getStyle(el, cssprop) {
- if (el.currentStyle) { //IE
- return el.currentStyle[cssprop];
- } else if ($window.getComputedStyle) {
- return $window.getComputedStyle(el)[cssprop];
- }
- // finally try and get inline style
- return el.style[cssprop];
- }
- /**
- * Checks if a given element is statically positioned
- * @param element - raw DOM element
- */
- function isStaticPositioned(element) {
- return (getStyle(element, 'position') || 'static' ) === 'static';
- }
- /**
- * returns the closest, non-statically positioned parentOffset of a given element
- * @param element
- */
- var parentOffsetEl = function(element) {
- var docDomEl = $document[0];
- var offsetParent = element.offsetParent || docDomEl;
- while (offsetParent && offsetParent !== docDomEl && isStaticPositioned(offsetParent) ) {
- offsetParent = offsetParent.offsetParent;
- }
- return offsetParent || docDomEl;
- };
- return {
- /**
- * Provides read-only equivalent of jQuery's position function:
- * http://api.jquery.com/position/
- */
- position: function(element) {
- var elBCR = this.offset(element);
- var offsetParentBCR = { top: 0, left: 0 };
- var offsetParentEl = parentOffsetEl(element[0]);
- if (offsetParentEl != $document[0]) {
- offsetParentBCR = this.offset(angular.element(offsetParentEl));
- offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop;
- offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft;
- }
- var boundingClientRect = element[0].getBoundingClientRect();
- return {
- width: boundingClientRect.width || element.prop('offsetWidth'),
- height: boundingClientRect.height || element.prop('offsetHeight'),
- top: elBCR.top - offsetParentBCR.top,
- left: elBCR.left - offsetParentBCR.left
- };
- },
- /**
- * Provides read-only equivalent of jQuery's offset function:
- * http://api.jquery.com/offset/
- */
- offset: function(element) {
- var boundingClientRect = element[0].getBoundingClientRect();
- return {
- width: boundingClientRect.width || element.prop('offsetWidth'),
- height: boundingClientRect.height || element.prop('offsetHeight'),
- top: boundingClientRect.top + ($window.pageYOffset || $document[0].documentElement.scrollTop),
- left: boundingClientRect.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft)
- };
- },
- /**
- * Provides coordinates for the targetEl in relation to hostEl
- */
- positionElements: function(hostEl, targetEl, positionStr, appendToBody) {
- var positionStrParts = positionStr.split('-');
- var pos0 = positionStrParts[0], pos1 = positionStrParts[1] || 'center';
- var hostElPos,
- targetElWidth,
- targetElHeight,
- targetElPos;
- hostElPos = appendToBody ? this.offset(hostEl) : this.position(hostEl);
- targetElWidth = targetEl.prop('offsetWidth');
- targetElHeight = targetEl.prop('offsetHeight');
- var shiftWidth = {
- center: function() {
- return hostElPos.left + hostElPos.width / 2 - targetElWidth / 2;
- },
- left: function() {
- return hostElPos.left;
- },
- right: function() {
- return hostElPos.left + hostElPos.width;
- }
- };
- var shiftHeight = {
- center: function() {
- return hostElPos.top + hostElPos.height / 2 - targetElHeight / 2;
- },
- top: function() {
- return hostElPos.top;
- },
- bottom: function() {
- return hostElPos.top + hostElPos.height;
- }
- };
- switch (pos0) {
- case 'right':
- targetElPos = {
- top: shiftHeight[pos1](),
- left: shiftWidth[pos0]()
- };
- break;
- case 'left':
- targetElPos = {
- top: shiftHeight[pos1](),
- left: hostElPos.left - targetElWidth
- };
- break;
- case 'bottom':
- targetElPos = {
- top: shiftHeight[pos0](),
- left: shiftWidth[pos1]()
- };
- break;
- default:
- targetElPos = {
- top: hostElPos.top - targetElHeight,
- left: shiftWidth[pos1]()
- };
- break;
- }
- return targetElPos;
- }
- };
- }]);
- angular.module('ui.bootstrap.dateparser', [])
- .service('dateParser', ['$log', '$locale', 'orderByFilter', function($log, $locale, orderByFilter) {
- // Pulled from https://github.com/mbostock/d3/blob/master/src/format/requote.js
- var SPECIAL_CHARACTERS_REGEXP = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;
- this.parsers = {};
- var formatCodeToRegex = {
- 'yyyy': {
- regex: '\\d{4}',
- apply: function(value) { this.year = +value; }
- },
- 'yy': {
- regex: '\\d{2}',
- apply: function(value) { this.year = +value + 2000; }
- },
- 'y': {
- regex: '\\d{1,4}',
- apply: function(value) { this.year = +value; }
- },
- 'MMMM': {
- regex: $locale.DATETIME_FORMATS.MONTH.join('|'),
- apply: function(value) { this.month = $locale.DATETIME_FORMATS.MONTH.indexOf(value); }
- },
- 'MMM': {
- regex: $locale.DATETIME_FORMATS.SHORTMONTH.join('|'),
- apply: function(value) { this.month = $locale.DATETIME_FORMATS.SHORTMONTH.indexOf(value); }
- },
- 'MM': {
- regex: '0[1-9]|1[0-2]',
- apply: function(value) { this.month = value - 1; }
- },
- 'M': {
- regex: '[1-9]|1[0-2]',
- apply: function(value) { this.month = value - 1; }
- },
- 'dd': {
- regex: '[0-2][0-9]{1}|3[0-1]{1}',
- apply: function(value) { this.date = +value; }
- },
- 'd': {
- regex: '[1-2]?[0-9]{1}|3[0-1]{1}',
- apply: function(value) { this.date = +value; }
- },
- 'EEEE': {
- regex: $locale.DATETIME_FORMATS.DAY.join('|')
- },
- 'EEE': {
- regex: $locale.DATETIME_FORMATS.SHORTDAY.join('|')
- },
- 'HH': {
- regex: '(?:0|1)[0-9]|2[0-3]',
- apply: function(value) { this.hours = +value; }
- },
- 'hh': {
- regex: '0[0-9]|1[0-2]',
- apply: function(value) { this.hours = +value; }
- },
- 'H': {
- regex: '1?[0-9]|2[0-3]',
- apply: function(value) { this.hours = +value; }
- },
- 'h': {
- regex: '[0-9]|1[0-2]',
- apply: function(value) { this.hours = +value; }
- },
- 'mm': {
- regex: '[0-5][0-9]',
- apply: function(value) { this.minutes = +value; }
- },
- 'm': {
- regex: '[0-9]|[1-5][0-9]',
- apply: function(value) { this.minutes = +value; }
- },
- 'sss': {
- regex: '[0-9][0-9][0-9]',
- apply: function(value) { this.milliseconds = +value; }
- },
- 'ss': {
- regex: '[0-5][0-9]',
- apply: function(value) { this.seconds = +value; }
- },
- 's': {
- regex: '[0-9]|[1-5][0-9]',
- apply: function(value) { this.seconds = +value; }
- },
- 'a': {
- regex: $locale.DATETIME_FORMATS.AMPMS.join('|'),
- apply: function(value) {
- if (this.hours === 12) {
- this.hours = 0;
- }
- if (value === 'PM') {
- this.hours += 12;
- }
- }
- }
- };
- function createParser(format) {
- var map = [], regex = format.split('');
- angular.forEach(formatCodeToRegex, function(data, code) {
- var index = format.indexOf(code);
- if (index > -1) {
- format = format.split('');
- regex[index] = '(' + data.regex + ')';
- format[index] = '$'; // Custom symbol to define consumed part of format
- for (var i = index + 1, n = index + code.length; i < n; i++) {
- regex[i] = '';
- format[i] = '$';
- }
- format = format.join('');
- map.push({ index: index, apply: data.apply });
- }
- });
- return {
- regex: new RegExp('^' + regex.join('') + '$'),
- map: orderByFilter(map, 'index')
- };
- }
- this.parse = function(input, format, baseDate) {
- if (!angular.isString(input) || !format) {
- return input;
- }
- format = $locale.DATETIME_FORMATS[format] || format;
- format = format.replace(SPECIAL_CHARACTERS_REGEXP, '\\$&');
- if (!this.parsers[format]) {
- this.parsers[format] = createParser(format);
- }
- var parser = this.parsers[format],
- regex = parser.regex,
- map = parser.map,
- results = input.match(regex);
- if (results && results.length) {
- var fields, dt;
- if (angular.isDate(baseDate) && !isNaN(baseDate.getTime())) {
- fields = {
- year: baseDate.getFullYear(),
- month: baseDate.getMonth(),
- date: baseDate.getDate(),
- hours: baseDate.getHours(),
- minutes: baseDate.getMinutes(),
- seconds: baseDate.getSeconds(),
- milliseconds: baseDate.getMilliseconds()
- };
- } else {
- if (baseDate) {
- $log.warn('dateparser:', 'baseDate is not a valid date');
- }
- fields = { year: 1900, month: 0, date: 1, hours: 0, minutes: 0, seconds: 0, milliseconds: 0 };
- }
- for (var i = 1, n = results.length; i < n; i++) {
- var mapper = map[i-1];
- if (mapper.apply) {
- mapper.apply.call(fields, results[i]);
- }
- }
- if (isValid(fields.year, fields.month, fields.date)) {
- dt = new Date(fields.year, fields.month, fields.date,
- fields.hours, fields.minutes, fields.seconds,
- fields.milliseconds || 0);
- }
- return dt;
- }
- };
- // Check if date is valid for specific month (and year for February).
- // Month: 0 = Jan, 1 = Feb, etc
- function isValid(year, month, date) {
- if (date < 1) {
- return false;
- }
- if (month === 1 && date > 28) {
- return date === 29 && ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0);
- }
- if (month === 3 || month === 5 || month === 8 || month === 10) {
- return date < 31;
- }
- return true;
- }
- }]);
- angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootstrap.position'])
- .value('$datepickerSuppressError', false)
- .constant('datepickerConfig', {
- formatDay: 'dd',
- formatMonth: 'MMMM',
- formatYear: 'yyyy',
- formatDayHeader: 'EEE',
- formatDayTitle: 'MMMM yyyy',
- formatMonthTitle: 'yyyy',
- datepickerMode: 'day',
- minMode: 'day',
- maxMode: 'year',
- showWeeks: true,
- startingDay: 0,
- yearRange: 20,
- minDate: null,
- maxDate: null,
- shortcutPropagation: false
- })
- .controller('DatepickerController', ['$scope', '$attrs', '$parse', '$interpolate', '$log', 'dateFilter', 'datepickerConfig', '$datepickerSuppressError', function($scope, $attrs, $parse, $interpolate, $log, dateFilter, datepickerConfig, $datepickerSuppressError) {
- var self = this,
- ngModelCtrl = { $setViewValue: angular.noop }; // nullModelCtrl;
- // Modes chain
- this.modes = ['day', 'month', 'year'];
- // Configuration attributes
- angular.forEach(['formatDay', 'formatMonth', 'formatYear', 'formatDayHeader', 'formatDayTitle', 'formatMonthTitle',
- 'showWeeks', 'startingDay', 'yearRange', 'shortcutPropagation'], function(key, index) {
- self[key] = angular.isDefined($attrs[key]) ? (index < 6 ? $interpolate($attrs[key])($scope.$parent) : $scope.$parent.$eval($attrs[key])) : datepickerConfig[key];
- });
- // Watchable date attributes
- angular.forEach(['minDate', 'maxDate'], function(key) {
- if ($attrs[key]) {
- $scope.$parent.$watch($parse($attrs[key]), function(value) {
- self[key] = value ? new Date(value) : null;
- self.refreshView();
- });
- } else {
- self[key] = datepickerConfig[key] ? new Date(datepickerConfig[key]) : null;
- }
- });
- angular.forEach(['minMode', 'maxMode'], function(key) {
- if ($attrs[key]) {
- $scope.$parent.$watch($parse($attrs[key]), function(value) {
- self[key] = angular.isDefined(value) ? value : $attrs[key];
- $scope[key] = self[key];
- 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]))) {
- $scope.datepickerMode = self[key];
- }
- });
- } else {
- self[key] = datepickerConfig[key] || null;
- $scope[key] = self[key];
- }
- });
- $scope.datepickerMode = $scope.datepickerMode || datepickerConfig.datepickerMode;
- $scope.uniqueId = 'datepicker-' + $scope.$id + '-' + Math.floor(Math.random() * 10000);
- if (angular.isDefined($attrs.initDate)) {
- this.activeDate = $scope.$parent.$eval($attrs.initDate) || new Date();
- $scope.$parent.$watch($attrs.initDate, function(initDate) {
- if (initDate && (ngModelCtrl.$isEmpty(ngModelCtrl.$modelValue) || ngModelCtrl.$invalid)) {
- self.activeDate = initDate;
- self.refreshView();
- }
- });
- } else {
- this.activeDate = new Date();
- }
- $scope.isActive = function(dateObject) {
- if (self.compare(dateObject.date, self.activeDate) === 0) {
- $scope.activeDateId = dateObject.uid;
- return true;
- }
- return false;
- };
- this.init = function(ngModelCtrl_) {
- ngModelCtrl = ngModelCtrl_;
- ngModelCtrl.$render = function() {
- self.render();
- };
- };
- this.render = function() {
- if (ngModelCtrl.$viewValue) {
- var date = new Date(ngModelCtrl.$viewValue),
- isValid = !isNaN(date);
- if (isValid) {
- this.activeDate = date;
- } else if (!$datepickerSuppressError) {
- $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.');
- }
- }
- this.refreshView();
- };
- this.refreshView = function() {
- if (this.element) {
- this._refreshView();
- var date = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null;
- ngModelCtrl.$setValidity('dateDisabled', !date || (this.element && !this.isDisabled(date)));
- }
- };
- this.createDateObject = function(date, format) {
- var model = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null;
- return {
- date: date,
- label: dateFilter(date, format),
- selected: model && this.compare(date, model) === 0,
- disabled: this.isDisabled(date),
- current: this.compare(date, new Date()) === 0,
- customClass: this.customClass(date)
- };
- };
- this.isDisabled = function(date) {
- return ((this.minDate && this.compare(date, this.minDate) < 0) || (this.maxDate && this.compare(date, this.maxDate) > 0) || ($attrs.dateDisabled && $scope.dateDisabled({date: date, mode: $scope.datepickerMode})));
- };
- this.customClass = function(date) {
- return $scope.customClass({date: date, mode: $scope.datepickerMode});
- };
- // Split array into smaller arrays
- this.split = function(arr, size) {
- var arrays = [];
- while (arr.length > 0) {
- arrays.push(arr.splice(0, size));
- }
- return arrays;
- };
- // Fix a hard-reprodusible bug with timezones
- // The bug depends on OS, browser, current timezone and current date
- // i.e.
- // var date = new Date(2014, 0, 1);
- // console.log(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours());
- // can result in "2013 11 31 23" because of the bug.
- this.fixTimeZone = function(date) {
- var hours = date.getHours();
- date.setHours(hours === 23 ? hours + 2 : 0);
- };
- $scope.select = function(date) {
- if ($scope.datepickerMode === self.minMode) {
- var dt = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : new Date(0, 0, 0, 0, 0, 0, 0);
- dt.setFullYear(date.getFullYear(), date.getMonth(), date.getDate());
- ngModelCtrl.$setViewValue(dt);
- ngModelCtrl.$render();
- } else {
- self.activeDate = date;
- $scope.datepickerMode = self.modes[self.modes.indexOf($scope.datepickerMode) - 1];
- }
- };
- $scope.move = function(direction) {
- var year = self.activeDate.getFullYear() + direction * (self.step.years || 0),
- month = self.activeDate.getMonth() + direction * (self.step.months || 0);
- self.activeDate.setFullYear(year, month, 1);
- self.refreshView();
- };
- $scope.toggleMode = function(direction) {
- direction = direction || 1;
- if (($scope.datepickerMode === self.maxMode && direction === 1) || ($scope.datepickerMode === self.minMode && direction === -1)) {
- return;
- }
- $scope.datepickerMode = self.modes[self.modes.indexOf($scope.datepickerMode) + direction];
- };
- // Key event mapper
- $scope.keys = { 13: 'enter', 32: 'space', 33: 'pageup', 34: 'pagedown', 35: 'end', 36: 'home', 37: 'left', 38: 'up', 39: 'right', 40: 'down' };
- var focusElement = function() {
- self.element[0].focus();
- };
- // Listen for focus requests from popup directive
- $scope.$on('datepicker.focus', focusElement);
- $scope.keydown = function(evt) {
- var key = $scope.keys[evt.which];
- if (!key || evt.shiftKey || evt.altKey) {
- return;
- }
- evt.preventDefault();
- if (!self.shortcutPropagation) {
- evt.stopPropagation();
- }
- if (key === 'enter' || key === 'space') {
- if (self.isDisabled(self.activeDate)) {
- return; // do nothing
- }
- $scope.select(self.activeDate);
- focusElement();
- } else if (evt.ctrlKey && (key === 'up' || key === 'down')) {
- $scope.toggleMode(key === 'up' ? 1 : -1);
- focusElement();
- } else {
- self.handleKeyDown(key, evt);
- self.refreshView();
- }
- };
- }])
- .directive('datepicker', function() {
- return {
- restrict: 'EA',
- replace: true,
- templateUrl: function(element, attrs) {
- return attrs.templateUrl || 'app/partials/bootstrap/datepicker.html';
- },
- scope: {
- datepickerMode: '=?',
- dateDisabled: '&',
- customClass: '&',
- shortcutPropagation: '&?'
- },
- require: ['datepicker', '^ngModel'],
- controller: 'DatepickerController',
- controllerAs: 'datepicker',
- link: function(scope, element, attrs, ctrls) {
- var datepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1];
- datepickerCtrl.init(ngModelCtrl);
- }
- };
- })
- .directive('daypicker', ['dateFilter', function(dateFilter) {
- return {
- restrict: 'EA',
- replace: true,
- templateUrl: 'app/partials/bootstrap/day.html',
- require: '^datepicker',
- link: function(scope, element, attrs, ctrl) {
- scope.showWeeks = ctrl.showWeeks;
- ctrl.step = { months: 1 };
- ctrl.element = element;
- var DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
- function getDaysInMonth(year, month) {
- return ((month === 1) && (year % 4 === 0) && ((year % 100 !== 0) || (year % 400 === 0))) ? 29 : DAYS_IN_MONTH[month];
- }
- function getDates(startDate, n) {
- var dates = new Array(n), current = new Date(startDate), i = 0, date;
- while (i < n) {
- date = new Date(current);
- ctrl.fixTimeZone(date);
- dates[i++] = date;
- current.setDate(current.getDate() + 1);
- }
- return dates;
- }
- ctrl._refreshView = function() {
- var year = ctrl.activeDate.getFullYear(),
- month = ctrl.activeDate.getMonth(),
- firstDayOfMonth = new Date(year, month, 1),
- difference = ctrl.startingDay - firstDayOfMonth.getDay(),
- numDisplayedFromPreviousMonth = (difference > 0) ? 7 - difference : - difference,
- firstDate = new Date(firstDayOfMonth);
- if (numDisplayedFromPreviousMonth > 0) {
- firstDate.setDate(-numDisplayedFromPreviousMonth + 1);
- }
- // 42 is the number of days on a six-month calendar
- var days = getDates(firstDate, 42);
- for (var i = 0; i < 42; i ++) {
- days[i] = angular.extend(ctrl.createDateObject(days[i], ctrl.formatDay), {
- secondary: days[i].getMonth() !== month,
- uid: scope.uniqueId + '-' + i
- });
- }
- scope.labels = new Array(7);
- for (var j = 0; j < 7; j++) {
- scope.labels[j] = {
- abbr: dateFilter(days[j].date, ctrl.formatDayHeader),
- full: dateFilter(days[j].date, 'EEEE')
- };
- }
- scope.title = dateFilter(ctrl.activeDate, ctrl.formatDayTitle);
- scope.rows = ctrl.split(days, 7);
- if (scope.showWeeks) {
- scope.weekNumbers = [];
- var thursdayIndex = (4 + 7 - ctrl.startingDay) % 7,
- numWeeks = scope.rows.length;
- for (var curWeek = 0; curWeek < numWeeks; curWeek++) {
- scope.weekNumbers.push(
- getISO8601WeekNumber(scope.rows[curWeek][thursdayIndex].date));
- }
- }
- };
- ctrl.compare = function(date1, date2) {
- return (new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()) - new Date(date2.getFullYear(), date2.getMonth(), date2.getDate()));
- };
- function getISO8601WeekNumber(date) {
- var checkDate = new Date(date);
- checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7)); // Thursday
- var time = checkDate.getTime();
- checkDate.setMonth(0); // Compare with Jan 1
- checkDate.setDate(1);
- return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
- }
- ctrl.handleKeyDown = function(key, evt) {
- var date = ctrl.activeDate.getDate();
- if (key === 'left') {
- date = date - 1; // up
- } else if (key === 'up') {
- date = date - 7; // down
- } else if (key === 'right') {
- date = date + 1; // down
- } else if (key === 'down') {
- date = date + 7;
- } else if (key === 'pageup' || key === 'pagedown') {
- var month = ctrl.activeDate.getMonth() + (key === 'pageup' ? - 1 : 1);
- ctrl.activeDate.setMonth(month, 1);
- date = Math.min(getDaysInMonth(ctrl.activeDate.getFullYear(), ctrl.activeDate.getMonth()), date);
- } else if (key === 'home') {
- date = 1;
- } else if (key === 'end') {
- date = getDaysInMonth(ctrl.activeDate.getFullYear(), ctrl.activeDate.getMonth());
- }
- ctrl.activeDate.setDate(date);
- };
- ctrl.refreshView();
- }
- };
- }])
- .directive('monthpicker', ['dateFilter', function(dateFilter) {
- return {
- restrict: 'EA',
- replace: true,
- templateUrl: 'app/partials/bootstrap/month.html',
- require: '^datepicker',
- link: function(scope, element, attrs, ctrl) {
- ctrl.step = { years: 1 };
- ctrl.element = element;
- ctrl._refreshView = function() {
- var months = new Array(12),
- year = ctrl.activeDate.getFullYear(),
- date;
- for (var i = 0; i < 12; i++) {
- date = new Date(year, i, 1);
- ctrl.fixTimeZone(date);
- months[i] = angular.extend(ctrl.createDateObject(date, ctrl.formatMonth), {
- uid: scope.uniqueId + '-' + i
- });
- }
- scope.title = dateFilter(ctrl.activeDate, ctrl.formatMonthTitle);
- scope.rows = ctrl.split(months, 3);
- };
- ctrl.compare = function(date1, date2) {
- return new Date(date1.getFullYear(), date1.getMonth()) - new Date(date2.getFullYear(), date2.getMonth());
- };
- ctrl.handleKeyDown = function(key, evt) {
- var date = ctrl.activeDate.getMonth();
- if (key === 'left') {
- date = date - 1; // up
- } else if (key === 'up') {
- date = date - 3; // down
- } else if (key === 'right') {
- date = date + 1; // down
- } else if (key === 'down') {
- date = date + 3;
- } else if (key === 'pageup' || key === 'pagedown') {
- var year = ctrl.activeDate.getFullYear() + (key === 'pageup' ? - 1 : 1);
- ctrl.activeDate.setFullYear(year);
- } else if (key === 'home') {
- date = 0;
- } else if (key === 'end') {
- date = 11;
- }
- ctrl.activeDate.setMonth(date);
- };
- ctrl.refreshView();
- }
- };
- }])
- .directive('yearpicker', ['dateFilter', function(dateFilter) {
- return {
- restrict: 'EA',
- replace: true,
- templateUrl: 'app/partials/bootstrap/year.html',
- require: '^datepicker',
- link: function(scope, element, attrs, ctrl) {
- var range = ctrl.yearRange;
- ctrl.step = { years: range };
- ctrl.element = element;
- function getStartingYear( year ) {
- return parseInt((year - 1) / range, 10) * range + 1;
- }
- ctrl._refreshView = function() {
- var years = new Array(range), date;
- for (var i = 0, start = getStartingYear(ctrl.activeDate.getFullYear()); i < range; i++) {
- date = new Date(start + i, 0, 1);
- ctrl.fixTimeZone(date);
- years[i] = angular.extend(ctrl.createDateObject(date, ctrl.formatYear), {
- uid: scope.uniqueId + '-' + i
- });
- }
- scope.title = [years[0].label, years[range - 1].label].join(' - ');
- scope.rows = ctrl.split(years, 5);
- };
- ctrl.compare = function(date1, date2) {
- return date1.getFullYear() - date2.getFullYear();
- };
- ctrl.handleKeyDown = function(key, evt) {
- var date = ctrl.activeDate.getFullYear();
- if (key === 'left') {
- date = date - 1; // up
- } else if (key === 'up') {
- date = date - 5; // down
- } else if (key === 'right') {
- date = date + 1; // down
- } else if (key === 'down') {
- date = date + 5;
- } else if (key === 'pageup' || key === 'pagedown') {
- date += (key === 'pageup' ? - 1 : 1) * ctrl.step.years;
- } else if (key === 'home') {
- date = getStartingYear(ctrl.activeDate.getFullYear());
- } else if (key === 'end') {
- date = getStartingYear(ctrl.activeDate.getFullYear()) + range - 1;
- }
- ctrl.activeDate.setFullYear(date);
- };
- ctrl.refreshView();
- }
- };
- }])
- .constant('datepickerPopupConfig', {
- datepickerPopup: 'yyyy-MM-dd',
- datepickerPopupTemplateUrl: 'app/partials/bootstrap/popup.html',
- datepickerTemplateUrl: 'app/partials/bootstrap/datepicker.html',
- html5Types: {
- date: 'yyyy-MM-dd',
- 'datetime-local': 'yyyy-MM-ddTHH:mm:ss.sss',
- 'month': 'yyyy-MM'
- },
- currentText: 'Today',
- clearText: 'Clear',
- closeText: 'Done',
- closeOnDateSelection: true,
- appendToBody: false,
- showButtonBar: true,
- onOpenFocus: true
- })
- .directive('datepickerPopup', ['$compile', '$parse', '$document', '$rootScope', '$position', 'dateFilter', 'dateParser', 'datepickerPopupConfig', '$timeout',
- function($compile, $parse, $document, $rootScope, $position, dateFilter, dateParser, datepickerPopupConfig, $timeout) {
- return {
- restrict: 'EA',
- require: 'ngModel',
- scope: {
- isOpen: '=?',
- currentText: '@',
- clearText: '@',
- closeText: '@',
- dateDisabled: '&',
- customClass: '&'
- },
- link: function(scope, element, attrs, ngModel) {
- var dateFormat,
- closeOnDateSelection = angular.isDefined(attrs.closeOnDateSelection) ? scope.$parent.$eval(attrs.closeOnDateSelection) : datepickerPopupConfig.closeOnDateSelection,
- appendToBody = angular.isDefined(attrs.datepickerAppendToBody) ? scope.$parent.$eval(attrs.datepickerAppendToBody) : datepickerPopupConfig.appendToBody,
- onOpenFocus = angular.isDefined(attrs.onOpenFocus) ? scope.$parent.$eval(attrs.onOpenFocus) : datepickerPopupConfig.onOpenFocus,
- datepickerPopupTemplateUrl = angular.isDefined(attrs.datepickerPopupTemplateUrl) ? attrs.datepickerPopupTemplateUrl : datepickerPopupConfig.datepickerPopupTemplateUrl,
- datepickerTemplateUrl = angular.isDefined(attrs.datepickerTemplateUrl) ? attrs.datepickerTemplateUrl : datepickerPopupConfig.datepickerTemplateUrl,
- cache = {};
- scope.showButtonBar = angular.isDefined(attrs.showButtonBar) ? scope.$parent.$eval(attrs.showButtonBar) : datepickerPopupConfig.showButtonBar;
- scope.getText = function(key) {
- return scope[key + 'Text'] || datepickerPopupConfig[key + 'Text'];
- };
- scope.isDisabled = function(date) {
- if (date === 'today') {
- date = new Date();
- }
- return ((scope.watchData.minDate && scope.compare(date, cache.minDate) < 0) ||
- (scope.watchData.maxDate && scope.compare(date, cache.maxDate) > 0));
- };
- scope.compare = function(date1, date2) {
- return (new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()) - new Date(date2.getFullYear(), date2.getMonth(), date2.getDate()));
- };
- var isHtml5DateInput = false;
- if (datepickerPopupConfig.html5Types[attrs.type]) {
- dateFormat = datepickerPopupConfig.html5Types[attrs.type];
- isHtml5DateInput = true;
- } else {
- dateFormat = attrs.datepickerPopup || datepickerPopupConfig.datepickerPopup;
- attrs.$observe('datepickerPopup', function(value, oldValue) {
- var newDateFormat = value || datepickerPopupConfig.datepickerPopup;
- // Invalidate the $modelValue to ensure that formatters re-run
- // FIXME: Refactor when PR is merged: https://github.com/angular/angular.js/pull/10764
- if (newDateFormat !== dateFormat) {
- dateFormat = newDateFormat;
- ngModel.$modelValue = null;
- if (!dateFormat) {
- throw new Error('datepickerPopup must have a date format specified.');
- }
- }
- });
- }
- if (!dateFormat) {
- throw new Error('datepickerPopup must have a date format specified.');
- }
- if (isHtml5DateInput && attrs.datepickerPopup) {
- throw new Error('HTML5 date input types do not support custom formats.');
- }
- // popup element used to display calendar
- var popupEl = angular.element('<div datepicker-popup-wrap><div datepicker></div></div>');
- popupEl.attr({
- 'ng-model': 'date',
- 'ng-change': 'dateSelection(date)',
- 'template-url': datepickerPopupTemplateUrl
- });
- function cameltoDash(string) {
- return string.replace(/([A-Z])/g, function($1) { return '-' + $1.toLowerCase(); });
- }
- // datepicker element
- var datepickerEl = angular.element(popupEl.children()[0]);
- datepickerEl.attr('template-url', datepickerTemplateUrl);
- if (isHtml5DateInput) {
- if (attrs.type === 'month') {
- datepickerEl.attr('datepicker-mode', '"month"');
- datepickerEl.attr('min-mode', 'month');
- }
- }
- if (attrs.datepickerOptions) {
- var options = scope.$parent.$eval(attrs.datepickerOptions);
- if (options && options.initDate) {
- scope.initDate = options.initDate;
- datepickerEl.attr('init-date', 'initDate');
- delete options.initDate;
- }
- angular.forEach(options, function(value, option) {
- datepickerEl.attr( cameltoDash(option), value );
- });
- }
- scope.watchData = {};
- angular.forEach(['minMode', 'maxMode', 'minDate', 'maxDate', 'datepickerMode', 'initDate', 'shortcutPropagation'], function(key) {
- if (attrs[key]) {
- var getAttribute = $parse(attrs[key]);
- scope.$parent.$watch(getAttribute, function(value) {
- scope.watchData[key] = value;
- if (key === 'minDate' || key === 'maxDate') {
- cache[key] = new Date(value);
- }
- });
- datepickerEl.attr(cameltoDash(key), 'watchData.' + key);
- // Propagate changes from datepicker to outside
- if (key === 'datepickerMode') {
- var setAttribute = getAttribute.assign;
- scope.$watch('watchData.' + key, function(value, oldvalue) {
- if (angular.isFunction(setAttribute) && value !== oldvalue) {
- setAttribute(scope.$parent, value);
- }
- });
- }
- }
- });
- if (attrs.dateDisabled) {
- datepickerEl.attr('date-disabled', 'dateDisabled({ date: date, mode: mode })');
- }
- if (attrs.showWeeks) {
- datepickerEl.attr('show-weeks', attrs.showWeeks);
- }
- if (attrs.customClass) {
- datepickerEl.attr('custom-class', 'customClass({ date: date, mode: mode })');
- }
- function parseDate(viewValue) {
- if (angular.isNumber(viewValue)) {
- // presumably timestamp to date object
- viewValue = new Date(viewValue);
- }
- if (!viewValue) {
- return null;
- } else if (angular.isDate(viewValue) && !isNaN(viewValue)) {
- return viewValue;
- } else if (angular.isString(viewValue)) {
- var date = dateParser.parse(viewValue, dateFormat, scope.date);
- if (isNaN(date)) {
- return undefined;
- } else {
- return date;
- }
- } else {
- return undefined;
- }
- }
- function validator(modelValue, viewValue) {
- var value = modelValue || viewValue;
- if (!attrs.ngRequired && !value) {
- return true;
- }
- if (angular.isNumber(value)) {
- value = new Date(value);
- }
- if (!value) {
- return true;
- } else if (angular.isDate(value) && !isNaN(value)) {
- return true;
- } else if (angular.isString(value)) {
- var date = dateParser.parse(value, dateFormat);
- return !isNaN(date);
- } else {
- return false;
- }
- }
- if (!isHtml5DateInput) {
- // Internal API to maintain the correct ng-invalid-[key] class
- ngModel.$$parserName = 'date';
- ngModel.$validators.date = validator;
- ngModel.$parsers.unshift(parseDate);
- ngModel.$formatters.push(function(value) {
- scope.date = value;
- return ngModel.$isEmpty(value) ? value : dateFilter(value, dateFormat);
- });
- } else {
- ngModel.$formatters.push(function(value) {
- scope.date = value;
- return value;
- });
- }
- // Inner change
- scope.dateSelection = function(dt) {
- if (angular.isDefined(dt)) {
- scope.date = dt;
- }
- var date = scope.date ? dateFilter(scope.date, dateFormat) : null; // Setting to NULL is necessary for form validators to function
- element.val(date);
- ngModel.$setViewValue(date);
- if (closeOnDateSelection) {
- scope.isOpen = false;
- element[0].focus();
- }
- };
- // Detect changes in the view from the text box
- ngModel.$viewChangeListeners.push(function() {
- scope.date = dateParser.parse(ngModel.$viewValue, dateFormat, scope.date);
- });
- var documentClickBind = function(event) {
- if (scope.isOpen && !(element[0].contains(event.target) || popupEl[0].contains(event.target))) {
- scope.$apply(function() {
- scope.isOpen = false;
- });
- }
- };
- var inputKeydownBind = function(evt) {
- if (evt.which === 27 && scope.isOpen) {
- evt.preventDefault();
- evt.stopPropagation();
- scope.$apply(function() {
- scope.isOpen = false;
- });
- element[0].focus();
- } else if (evt.which === 40 && !scope.isOpen) {
- evt.preventDefault();
- evt.stopPropagation();
- scope.$apply(function() {
- scope.isOpen = true;
- });
- }
- };
- element.bind('keydown', inputKeydownBind);
- scope.keydown = function(evt) {
- if (evt.which === 27) {
- scope.isOpen = false;
- element[0].focus();
- }
- };
- scope.$watch('isOpen', function(value) {
- if (value) {
- scope.position = appendToBody ? $position.offset(element) : $position.position(element);
- scope.position.top = scope.position.top + element.prop('offsetHeight');
- $timeout(function() {
- if (onOpenFocus) {
- scope.$broadcast('datepicker.focus');
- }
- $document.bind('click', documentClickBind);
- }, 0, false);
- } else {
- $document.unbind('click', documentClickBind);
- }
- });
- scope.select = function(date) {
- if (date === 'today') {
- var today = new Date();
- if (angular.isDate(scope.date)) {
- date = new Date(scope.date);
- date.setFullYear(today.getFullYear(), today.getMonth(), today.getDate());
- } else {
- date = new Date(today.setHours(0, 0, 0, 0));
- }
- }
- scope.dateSelection(date);
- };
- scope.close = function() {
- scope.isOpen = false;
- element[0].focus();
- };
- var $popup = $compile(popupEl)(scope);
- // Prevent jQuery cache memory leak (template is now redundant after linking)
- popupEl.remove();
- if (appendToBody) {
- $document.find('body').append($popup);
- } else {
- element.after($popup);
- }
- scope.$on('$destroy', function() {
- if (scope.isOpen === true) {
- if (!$rootScope.$$phase) {
- scope.$apply(function() {
- scope.isOpen = false;
- });
- }
- }
- $popup.remove();
- element.unbind('keydown', inputKeydownBind);
- $document.unbind('click', documentClickBind);
- });
- }
- };
- }])
- .directive('datepickerPopupWrap', function() {
- return {
- restrict:'EA',
- replace: true,
- transclude: true,
- templateUrl: function(element, attrs) {
- return attrs.templateUrl || 'template/datepicker/popup.html';
- }
- };
- });
- /**
- * @ngdoc overview
- * @name ui.bootstrap.tabs
- *
- * @description
- * AngularJS version of the tabs directive.
- */
- angular.module('ui.bootstrap.tabs', [])
- .controller('TabsetController', ['$scope', function TabsetCtrl($scope) {
- var ctrl = this,
- tabs = ctrl.tabs = $scope.tabs = [];
- ctrl.select = function(selectedTab) {
- angular.forEach(tabs, function(tab) {
- if (tab.active && tab !== selectedTab) {
- tab.active = false;
- tab.onDeselect();
- selectedTab.selectCalled = false;
- }
- });
- selectedTab.active = true;
- // only call select if it has not already been called
- if (!selectedTab.selectCalled) {
- selectedTab.onSelect();
- selectedTab.selectCalled = true;
- }
- };
- ctrl.addTab = function addTab(tab) {
- tabs.push(tab);
- // we can't run the select function on the first tab
- // since that would select it twice
- if (tabs.length === 1 && tab.active !== false) {
- tab.active = true;
- } else if (tab.active) {
- ctrl.select(tab);
- } else {
- tab.active = false;
- }
- };
- ctrl.removeTab = function removeTab(tab) {
- var index = tabs.indexOf(tab);
- //Select a new tab if the tab to be removed is selected and not destroyed
- if (tab.active && tabs.length > 1 && !destroyed) {
- //If this is the last tab, select the previous tab. else, the next tab.
- var newActiveIndex = index == tabs.length - 1 ? index - 1 : index + 1;
- ctrl.select(tabs[newActiveIndex]);
- }
- tabs.splice(index, 1);
- };
- var destroyed;
- $scope.$on('$destroy', function() {
- destroyed = true;
- });
- }])
- /**
- * @ngdoc directive
- * @name ui.bootstrap.tabs.directive:tabset
- * @restrict EA
- *
- * @description
- * Tabset is the outer container for the tabs directive
- *
- * @param {boolean=} vertical Whether or not to use vertical styling for the tabs.
- * @param {boolean=} justified Whether or not to use justified styling for the tabs.
- *
- * @example
- <example module="ui.bootstrap">
- <file name="index.html">
- <tabset>
- <tab heading="Tab 1"><b>First</b> Content!</tab>
- <tab heading="Tab 2"><i>Second</i> Content!</tab>
- </tabset>
- <hr />
- <tabset vertical="true">
- <tab heading="Vertical Tab 1"><b>First</b> Vertical Content!</tab>
- <tab heading="Vertical Tab 2"><i>Second</i> Vertical Content!</tab>
- </tabset>
- <tabset justified="true">
- <tab heading="Justified Tab 1"><b>First</b> Justified Content!</tab>
- <tab heading="Justified Tab 2"><i>Second</i> Justified Content!</tab>
- </tabset>
- </file>
- </example>
- */
- .directive('tabset', function() {
- return {
- restrict: 'EA',
- transclude: true,
- replace: true,
- scope: {
- type: '@'
- },
- controller: 'TabsetController',
- templateUrl: 'app/partials/bootstrap/tabset.html',
- link: function(scope, element, attrs) {
- scope.vertical = angular.isDefined(attrs.vertical) ? scope.$parent.$eval(attrs.vertical) : false;
- scope.justified = angular.isDefined(attrs.justified) ? scope.$parent.$eval(attrs.justified) : false;
- }
- };
- })
- /**
- * @ngdoc directive
- * @name ui.bootstrap.tabs.directive:tab
- * @restrict EA
- *
- * @param {string=} heading The visible heading, or title, of the tab. Set HTML headings with {@link ui.bootstrap.tabs.directive:tabHeading tabHeading}.
- * @param {string=} select An expression to evaluate when the tab is selected.
- * @param {boolean=} active A binding, telling whether or not this tab is selected.
- * @param {boolean=} disabled A binding, telling whether or not this tab is disabled.
- *
- * @description
- * Creates a tab with a heading and content. Must be placed within a {@link ui.bootstrap.tabs.directive:tabset tabset}.
- *
- * @example
- <example module="ui.bootstrap">
- <file name="index.html">
- <div ng-controller="TabsDemoCtrl">
- <button class="btn btn-small" ng-click="items[0].active = true">
- Select item 1, using active binding
- </button>
- <button class="btn btn-small" ng-click="items[1].disabled = !items[1].disabled">
- Enable/disable item 2, using disabled binding
- </button>
- <br />
- <tabset>
- <tab heading="Tab 1">First Tab</tab>
- <tab select="alertMe()">
- <tab-heading><i class="icon-bell"></i> Alert me!</tab-heading>
- Second Tab, with alert callback and html heading!
- </tab>
- <tab ng-repeat="item in items"
- heading="{{item.title}}"
- disabled="item.disabled"
- active="item.active">
- {{item.content}}
- </tab>
- </tabset>
- </div>
- </file>
- <file name="script.js">
- function TabsDemoCtrl($scope) {
- $scope.items = [
- { title:"Dynamic Title 1", content:"Dynamic Item 0" },
- { title:"Dynamic Title 2", content:"Dynamic Item 1", disabled: true }
- ];
- $scope.alertMe = function() {
- setTimeout(function() {
- alert("You've selected the alert tab!");
- });
- };
- };
- </file>
- </example>
- */
- /**
- * @ngdoc directive
- * @name ui.bootstrap.tabs.directive:tabHeading
- * @restrict EA
- *
- * @description
- * Creates an HTML heading for a {@link ui.bootstrap.tabs.directive:tab tab}. Must be placed as a child of a tab element.
- *
- * @example
- <example module="ui.bootstrap">
- <file name="index.html">
- <tabset>
- <tab>
- <tab-heading><b>HTML</b> in my titles?!</tab-heading>
- And some content, too!
- </tab>
- <tab>
- <tab-heading><i class="icon-heart"></i> Icon heading?!?</tab-heading>
- That's right.
- </tab>
- </tabset>
- </file>
- </example>
- */
- .directive('tab', ['$parse', '$log', function($parse, $log) {
- return {
- require: '^tabset',
- restrict: 'EA',
- replace: true,
- templateUrl: 'app/partials/bootstrap/tab.html',
- transclude: true,
- scope: {
- active: '=?',
- heading: '@',
- onSelect: '&select', //This callback is called in contentHeadingTransclude
- //once it inserts the tab's content into the dom
- onDeselect: '&deselect'
- },
- controller: function() {
- //Empty controller so other directives can require being 'under' a tab
- },
- link: function(scope, elm, attrs, tabsetCtrl, transclude) {
- scope.$watch('active', function(active) {
- if (active) {
- tabsetCtrl.select(scope);
- }
- });
- scope.disabled = false;
- if (attrs.disable) {
- scope.$parent.$watch($parse(attrs.disable), function(value) {
- scope.disabled = !! value;
- });
- }
- // Deprecation support of "disabled" parameter
- // fix(tab): IE9 disabled attr renders grey text on enabled tab #2677
- // This code is duplicated from the lines above to make it easy to remove once
- // the feature has been completely deprecated
- if (attrs.disabled) {
- $log.warn('Use of "disabled" attribute has been deprecated, please use "disable"');
- scope.$parent.$watch($parse(attrs.disabled), function(value) {
- scope.disabled = !! value;
- });
- }
- scope.select = function() {
- if (!scope.disabled) {
- scope.active = true;
- }
- };
- tabsetCtrl.addTab(scope);
- scope.$on('$destroy', function() {
- tabsetCtrl.removeTab(scope);
- });
- //We need to transclude later, once the content container is ready.
- //when this link happens, we're inside a tab heading.
- scope.$transcludeFn = transclude;
- }
- };
- }])
- .directive('tabHeadingTransclude', function() {
- return {
- restrict: 'A',
- require: '^tab',
- link: function(scope, elm, attrs, tabCtrl) {
- scope.$watch('headingElement', function updateHeadingElement(heading) {
- if (heading) {
- elm.html('');
- elm.append(heading);
- }
- });
- }
- };
- })
- .directive('tabContentTransclude', function() {
- return {
- restrict: 'A',
- require: '^tabset',
- link: function(scope, elm, attrs) {
- var tab = scope.$eval(attrs.tabContentTransclude);
- //Now our tab is ready to be transcluded: both the tab heading area
- //and the tab content area are loaded. Transclude 'em both.
- tab.$transcludeFn(tab.$parent, function(contents) {
- angular.forEach(contents, function(node) {
- if (isTabHeading(node)) {
- //Let tabHeadingTransclude know.
- tab.headingElement = node;
- } else {
- elm.append(node);
- }
- });
- });
- }
- };
- function isTabHeading(node) {
- return node.tagName && (
- node.hasAttribute('tab-heading') ||
- node.hasAttribute('data-tab-heading') ||
- node.hasAttribute('x-tab-heading') ||
- node.tagName.toLowerCase() === 'tab-heading' ||
- node.tagName.toLowerCase() === 'data-tab-heading' ||
- node.tagName.toLowerCase() === 'x-tab-heading'
- );
- }
- });
|