import {Component} from '@angular/core';
import {NgbDate} from './dp/ngb-date';

const now = new Date();

@Component({
  selector: 'my-app',
  styles: [`
    .day {      
      text-align: center;
      padding: 0.25rem 0.35rem;
      border-radius: 1rem;
      background-color: lightgoldenrodyellow;
    }
    .day.odd {
      background-color: lightblue;
    }
    .icon {
      width: 2rem;
      height: 2rem;
    }
    .day.selected, .day.selected:hover {
      background-color: red;
    }
    .day.greyed {
      color: lightgray;
    }
    .day:hover {
      background-color: lightsalmon;
      cursor: pointer;
    }
  `],
  templateUrl: 'app/main.html'
})
export class AppComponent {

  WEEKDAYS = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];

  date = {year: 2015, month: 2};
  minDate = {year: 2014, month: 5, date: 10};
  maxDate = {year: 2018, month: 5, date: 10};

  showNavigation = true;
  showWeekNumbers = false;
  showWeekdays = true;
  firstDayOfWeek = 1;
  
  model1 = null;
  model2 = null;

  setDate() {
    this.model1 = {year: now.getFullYear(), month: now.getMonth(), date: now.getDate()};
  }

  markDisabled(day: {year: number, month: number, date: number}): boolean {
    let today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
    let date = new Date(day.year, day.month, day.date);
    return date < today;
  }
  
  markDisabled2(date: {year: number, month: number, date: number}): boolean {
    return date.date <= 3;
  }  
}
<!DOCTYPE html>
<html>
  <head>
    <title>NgbDatepicker WIP</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="http://v4-alpha.getbootstrap.com/dist/css/bootstrap.min.css" />

    <!-- 1. Load libraries -->
     <!-- Polyfill(s) for older browsers -->
    <script src="https://npmcdn.com/core-js/client/shim.min.js"></script>

    <script src="https://npmcdn.com/zone.js@0.6.12?main=browser"></script>
    <script src="https://npmcdn.com/reflect-metadata@0.1.3"></script>
    <script src="https://npmcdn.com/systemjs@0.19.27/dist/system.src.js"></script>

    <!-- 2. Configure SystemJS -->
    <script src="systemjs.config.js"></script>
    <script>
      System.import('app').catch(function(err){ console.error(err); });
    </script>
  </head>

  <!-- 3. Display the application -->
  <body class="container">
    <my-app>Loading...</my-app>
  </body>
</html>


<!-- 
Copyright 2016 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
-->
/**
 * PLUNKER VERSION (based on systemjs.config.js in angular.io)
 * System configuration for Angular 2 samples
 * Adjust as necessary for your application needs.
 */
(function(global) {

  var ngVer = '@2.0.0-rc.5'; // lock in the angular package version; do not let it float to current!

  //map tells the System loader where to look for things
  var  map = {
    'app':                        'app',
    '@angular':                   'https://npmcdn.com/@angular', // sufficient if we didn't pin the version
    '@angular/forms':             'https://npmcdn.com/@angular/forms@0.3.0',    
    'angular2-in-memory-web-api': 'https://npmcdn.com/angular2-in-memory-web-api', // get latest
    'rxjs':                       'https://npmcdn.com/rxjs@5.0.0-beta.6',
    'ts':                         'https://npmcdn.com/plugin-typescript@4.0.10/lib/plugin.js',
    'typescript':                 'https://npmcdn.com/typescript@1.8.10/lib/typescript.js',
 };

  //packages tells the System loader how to load when no filename and/or no extension
  var packages = {
    'app':                        { main: 'bootstrap.ts',  defaultExtension: 'ts' },
    'rxjs':                       { defaultExtension: 'js' },
    'angular2-in-memory-web-api': { main: 'index.js', defaultExtension: 'js' },
  };

  var ngPackageNames = [
    'common',
    'compiler',
    'core',
    'http',
    'platform-browser',
    'platform-browser-dynamic',
    'router-deprecated',
    'upgrade',
  ];

  // Add map entries for each angular package
  // only because we're pinning the version with `ngVer`.
  ngPackageNames.forEach(function(pkgName) {
    map['@angular/'+pkgName] = 'https://npmcdn.com/@angular/' + pkgName + ngVer;
  });

  // Add package entries for angular packages
  ngPackageNames.forEach(function(pkgName) {

    // Bundled (~40 requests):
    packages['@angular/'+pkgName] = { main: '/bundles/' + pkgName + '.umd.js', defaultExtension: 'js' };

    // Individual files (~300 requests):
    //packages['@angular/'+pkgName] = { main: 'index.js', defaultExtension: 'js' };
  });
  
  packages['@angular/forms'] = { main: 'index.js', defaultExtension: 'js' };

  var config = {
    // DEMO ONLY! REAL CODE SHOULD NOT TRANSPILE IN THE BROWSER
    transpiler: 'ts',
    typescriptOptions: {
      tsconfig: true
    },
    meta: {
      'typescript': {
        "exports": "ts"
      }
    },
    map: map,
    packages: packages
  }

  System.config(config);

})(this);


/*
Copyright 2016 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
*/
{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "moduleResolution": "node",
    "sourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "removeComments": false,
    "noImplicitAny": true,
    "suppressImplicitAnyIndexErrors": true
  }
}
<div class="container">
  
  <p>Standard template, disabled dates:</p>

  <ngb-datepicker #dp
    [showNavigation]="showNavigation"
    [showWeekNumbers]="showWeekNumbers"
    [showWeekdays]="showWeekdays"
    [firstDayOfWeek]="firstDayOfWeek"
    [minDate]="minDate"
    [maxDate]="maxDate"
    [markDisabled]="markDisabled"
    [(ngModel)]="model1">
  </ngb-datepicker>

  <div>Model: {{ model1 | json }}</div>

  <button class="btn btn-sm btn-primary" (click)="setDate()">Select Today</button>
  <button class="btn btn-sm btn-primary" (click)="dp.navigateTo()">Show current month</button>

  <hr/>

  <p>Custom date templates, disabled dates:</p>
  
  <template #tpl let-day="day" let-month="month" let-selected="selected">
    <div *ngIf="day.date.date !== 13 && !day.disabled" 
      class="day" 
      [class.odd]="day.date.date % 2 === 0" 
      [class.selected]="selected"
      [class.greyed]="day.date.month !== month.number">{{ day.date.date }}
    </div>
    <img class="icon" *ngIf="day.date.date === 13" [src]="'https://upload.wikimedia.org/wikipedia/commons/thumb/c/c2/URSS-Russian_aviation_red_star.svg/200px-URSS-Russian_aviation_red_star.svg.png'">
  </template>

  <ngb-datepicker
    [startDate]="date"
    [showNavigation]="showNavigation"
    [showWeekNumbers]="showWeekNumbers"
    [showWeekdays]="showWeekdays"
    [firstDayOfWeek]="firstDayOfWeek"
    [minDate]="minDate"
    [maxDate]="maxDate"        
    [dayTemplate]="tpl"
    [markDisabled]="markDisabled2"
    [(ngModel)]="model2">
  </ngb-datepicker>

  <div>Model: {{ model2 | json }}</div>

  <hr/>

  <div>
  <label>
    <input type="checkbox" [(ngModel)]="showNavigation"> Show navigation
  </label>
  </div>

  <div>
  <label>
    <input type="checkbox" [(ngModel)]="showWeekNumbers"> Show week numbers
  </label>
  </div>

  <div>
    <label>
      <input type="checkbox" [(ngModel)]="showWeekdays"> Show weekdays
    </label>
  </div>

  <div>
  <label>
    First day of week
    <select [(ngModel)]="firstDayOfWeek">
      <option *ngFor="let w of WEEKDAYS; let i = index;" [value]="i">{{ w }}</option>
    </select>
  </label>
  </div>

</div>
import {NgModule} from '@angular/core';
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {BrowserModule} from '@angular/platform-browser';
import {FormsModule} from '@angular/forms';
import {NgbDatepickerModule} from './dp/datepicker.module';

import {AppComponent} from './main';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    NgbDatepickerModule,
    FormsModule
  ],
  bootstrap: [AppComponent]
})
export class AppModule {
}

platformBrowserDynamic().bootstrapModule(AppModule);
import {Component, Input, OnChanges, SimpleChanges, TemplateRef, forwardRef, OnInit} from '@angular/core';
import {NG_VALUE_ACCESSOR, ControlValueAccessor} from '@angular/forms';
import {NgbCalendar} from './ngb-calendar';
import {NgbDate} from './ngb-date';
import {NgbDatepickerService} from './datepicker-service';
import {MonthViewModel, DayTemplateContext, NavigationEvent} from './datepicker-view-model';
import {toInteger} from '../util';

const NGB_DATEPICKER_VALUE_ACCESSOR = {
  provide: NG_VALUE_ACCESSOR,
  useExisting: forwardRef(() => NgbDatepicker),
  multi: true
};

/**
 * A lightweight and highly configurable datepicker directive
 */
@Component({
  exportAs: 'ngbDatepicker',
  selector: 'ngb-datepicker',
  template: `
    <template #dt let-day="day" let-month="month" let-selected="selected">
       <div ngb-datepicker-day-view [day]="day" [month]="month" [selected]="selected"></div>
    </template>
    
    <table>
      <tbody *ngIf="showNavigation" ngb-datepicker-navigation
        [date]="_date"
        [minDate]="_minDate"
        [maxDate]="_maxDate"
        [type]="navigation"
        [showWeekNumbers]="showWeekNumbers"
        (navigate)="onNavigateEvent($event)"
        (select)="onNavigateDateSelect($event)">
      </tbody>
      
      <tbody ngb-datepicker-month-view
        [month]="month"
        [selectedDate]="model"
        [dayTemplate]="dayTemplate || dt"
        [showWeekdays]="showWeekdays"
        [showWeekNumbers]="showWeekNumbers"
        (select)="onDateSelect($event)">
      </tbody>
    </table>
  `,
  providers: [NGB_DATEPICKER_VALUE_ACCESSOR]
})
export class NgbDatepicker implements OnChanges,
    OnInit, ControlValueAccessor {
  _date: NgbDate;
  _minDate: NgbDate;
  _maxDate: NgbDate;

  month: MonthViewModel;
  model: NgbDate;

  /**
   * Reference for the custom template for the day display
   */
  @Input() dayTemplate: TemplateRef<DayTemplateContext>;

  /**
   * First day of the week, 0=Sun, 1=Mon, etc.
   */
  @Input() firstDayOfWeek = 1;

  /**
   * Callback to mark a given date as disabled
   */
  @Input() markDisabled: (date: {year: number, month: number, date: number}) => boolean;

  /**
   * Min date for the navigation. Works only with navigation type 'select'
   */
  @Input() minDate: {year: number, month: number, date: number};

  /**
   * Max date for the navigation. Works only with navigation type 'select'
   */
  @Input() maxDate: {year: number, month: number, date: number};

  /**
   * Navigation type. 'select' is a default one and uses selectboxes fot month and year. Other types might be supported
   * in future
   */
  @Input() navigation = 'select';

  /**
   * Whether to display navigation or not
   */
  @Input() showNavigation = true;

  /**
   * Whether to display days of the week
   */
  @Input() showWeekdays = true;

  /**
   * Whether to display week numbers
   */
  @Input() showWeekNumbers = false;

  /**
   * Date to open calendar with. If nothing provided, calendar will open with current month.
   * Use 'navigateTo(date)' as an alternative
   */
  @Input() startDate: {year: number, month: number};

  onChange = (_: any) => {};
  onTouched = () => {};

  constructor(private _service: NgbDatepickerService, private _calendar: NgbCalendar) {}

  /**
   * Navigates current view to provided date. If nothing provided calendat will open current month.
   * Use 'date' input as an alternative
   */
  navigateTo(date?: {year: number, month: number}) {
    this._setViewWithinLimits(date ? NgbDate.from(date) : this._calendar.getToday());
    this._updateData();
  }

  ngOnInit() {
    this._maxDate = NgbDate.from(this.maxDate);
    this._minDate = NgbDate.from(this.minDate);

    if ((!this._maxDate || !this._minDate) && this.navigation === 'select') {
      throw new Error(`Both 'maxDate' and 'minDate' must be provided for navigation type 'select'`);
    }

    if (this._minDate && this._maxDate && this._maxDate.before(this._minDate)) {
      throw new Error(`'maxDate' should be greater than 'minDate'`);
    }
  }

  ngOnChanges(changes: SimpleChanges) {
    this._maxDate = NgbDate.from(this.maxDate);
    this._minDate = NgbDate.from(this.minDate);
    this.navigateTo(this.startDate);
  }

  /**
   * @internal
   */
  onDateSelect(date: NgbDate) {
    this._setViewWithinLimits(date);

    this.onTouched();
    this.writeValue(date);
    this.onChange({year: date.year, month: date.month, date: date.date});

    // switch current month
    if (this._date.month !== this.month.number) {
      this._updateData();
    }
  }

  /**
   * @internal
   */
  onNavigateDateSelect(date: NgbDate) {
    this._setViewWithinLimits(date);
    this._updateData();
  }

  /**
   * @internal
   */
  onNavigateEvent(event: NavigationEvent) {
    switch (event) {
      case NavigationEvent.PREV:
        this._setViewWithinLimits(this._calendar.getPrevMonth(this._date));
        break;
      case NavigationEvent.NEXT:
        this._setViewWithinLimits(this._calendar.getNextMonth(this._date));
        break;
    }

    this._updateData();
  }

  /**
   * @internal
   */
  registerOnChange(fn: (value: any) => any): void { this.onChange = fn; }

  /**
   * @internal
   */
  registerOnTouched(fn: () => any): void { this.onTouched = fn; }

  /**
   * @internal
   */
  writeValue(value) { this.model = value ? new NgbDate(value.year, value.month, value.date) : null; }

  private _setViewWithinLimits(date: NgbDate) {
    if (this._minDate && date.before(this._minDate)) {
      this._date = new NgbDate(this._minDate.year, this._minDate.month, 1);
    } else if (this._maxDate && date.after(this._maxDate)) {
      this._date = new NgbDate(this._maxDate.year, this._maxDate.month, 1);
    } else {
      this._date = new NgbDate(date.year, date.month, 1);
    }
  }

  private _updateData() {
    this.month = this._service.generateMonthViewModel(
        this._date, this._minDate, this._maxDate, toInteger(this.firstDayOfWeek), this.markDisabled);
  }
}
import {Component, Input, TemplateRef, Output, EventEmitter} from '@angular/core';
import {MonthViewModel, DayViewModel, DayTemplateContext} from './datepicker-view-model';
import {NgbDate} from './ngb-date';
import {NgbDatepickerI18n} from './datepicker-i18n';

@Component({
  selector: '[ngb-datepicker-month-view]',
  styles: [`
    .weekday {
      padding-bottom: 0.25rem;
    }
    .weeknumber {    
    }
    .day {
      padding: 0;
      height: 100%;
      cursor: pointer;
    }
    .day.disabled {
      cursor: not-allowed;
    }
  `],
  template: `
    <tr *ngIf="showWeekdays">
      <td *ngIf="showWeekNumbers"></td>
      <td *ngFor="let w of month.weekdays" class="weekday text-xs-center font-weight-bold">{{ i18n.getWeekdayName(w) }}</td>
    </tr>
    <tr *ngFor="let week of month.weeks">
      <td *ngIf="showWeekNumbers" class="weeknumber small text-xs-center">{{ week.number }}</td>
      <td *ngFor="let day of week.days" (click)="doSelect(day)" class="day" [class.disabled]="day.disabled">
        <template [ngTemplateOutlet]="dayTemplate" 
        [ngOutletContext]="{day: day, month: month, selected: selectedDate && selectedDate.equals(day.date)}"></template>
      </td>                
    </tr>
  `
})
export class NgbDatepickerMonthView {
  @Input() month: MonthViewModel;
  @Input() selectedDate: NgbDate;
  @Input() dayTemplate: TemplateRef<DayTemplateContext>;
  @Input() showWeekdays;
  @Input() showWeekNumbers;

  @Output() select = new EventEmitter<NgbDate>();

  constructor(public i18n: NgbDatepickerI18n) {}

  doSelect(day: DayViewModel) {
    if (!day.disabled) {
      this.select.emit(NgbDate.from(day.date));
    }
  }
}
export function toInteger(value: any): number {
  return parseInt(`${value}`, 10);
}

export function toString(value: any): string {
  return (value !== undefined && value !== null) ? `${value}` : '';
}

export function getValueInRange(value: number, max: number, min = 0): number {
  return Math.max(Math.min(value, max), min);
}

export function isString(value: any): boolean {
  return typeof value === 'string';
}

export function isNumber(value: any): boolean {
  return !isNaN(toInteger(value));
}

export function padNumber(value: number) {
  if (isNumber(value)) {
    return `0${value}`.slice(-2);
  } else {
    return '';
  }
}

export function regExpEscape(text) {
  return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
}
import {Component, Input, Output, EventEmitter} from '@angular/core';
import {NavigationEvent} from './datepicker-view-model';
import {NgbDate} from './ngb-date';
import {NgbDatepickerI18n} from './datepicker-i18n';
import {NgbCalendar} from './ngb-calendar';

@Component({
  selector: '[ngb-datepicker-navigation]',
  styles: [`
    td {
      text-align: center;
      padding-bottom: 0.25rem;
    }
  `],
  template: `
    <tr>
      <td>
        <button (click)="doNavigate(navigation.PREV)" class="btn btn-sm btn-secondary btn-block" [disabled]="prevDisabled()">&lt;</button>
      </td>
      <td [attr.colspan]="showWeekNumbers ? 6 : 5">      
        <ngb-datepicker-navigation-select *ngIf="type === 'select'"
          [date]="date"
          [minYear]="minDate.year"
          [maxYear]="maxDate.year"
          (select)="selectDate($event)">
        </ngb-datepicker-navigation-select>
      </td>
      <td>
        <button (click)="doNavigate(navigation.NEXT)" class="btn btn-sm btn-secondary btn-block" [disabled]="nextDisabled()">&gt;</button>
      </td>
    </tr>
  `
})
export class NgbDatepickerNavigation {
  navigation = NavigationEvent;

  @Input() type: string;
  @Input() showWeekNumbers: boolean;

  @Input() date: NgbDate;
  @Input() minDate: NgbDate;
  @Input() maxDate: NgbDate;

  @Output() navigate = new EventEmitter<NavigationEvent>();
  @Output() select = new EventEmitter<NgbDate>();

  constructor(public i18n: NgbDatepickerI18n, private _calendar: NgbCalendar) {}

  doNavigate(event: NavigationEvent) { this.navigate.emit(event); }

  nextDisabled() { return !this.maxDate ? false : this._calendar.getNextMonth(this.date).after(this.maxDate); }

  prevDisabled() { return !this.minDate ? false : this._calendar.getPrevMonth(this.date).before(this.minDate); }

  selectDate(date: NgbDate) { this.select.emit(date); }
}
import {NgbCalendar} from './ngb-calendar';
import {NgbDate} from './ngb-date';
import {MonthViewModel, DayViewModel} from './datepicker-view-model';
import {Injectable} from '@angular/core';

@Injectable()
export class NgbDatepickerService {
  constructor(private _calendar: NgbCalendar) {}

  generateMonthViewModel(
      date: NgbDate, minDate: NgbDate, maxDate: NgbDate, firstDayOfWeek: number,
      markDisabled: (date: NgbDate) => boolean): MonthViewModel {
    const month: MonthViewModel = {number: date.month, year: date.year, weeks: [], weekdays: []};

    date = this._getFirstViewDate(date, firstDayOfWeek);

    // month has weeks
    for (let w = 0; w < this._calendar.getWeeksPerMonth(); w++) {
      const days: DayViewModel[] = [];

      // week has days
      for (let d = 0; d < this._calendar.getDaysPerWeek(); d++) {
        if (w === 0) {
          month.weekdays.push(this._calendar.getWeekday(date));
        }

        const newDate = new NgbDate(date.year, date.month, date.date);

        let disabled = (minDate && newDate.before(minDate)) || (maxDate && newDate.after(maxDate));
        if (!disabled && markDisabled) {
          disabled = markDisabled(newDate);
        }

        days.push({date: {year: date.year, month: date.month, date: date.date}, disabled: disabled});

        date = this._calendar.getNextDate(date);
      }

      month.weeks.push(
          {number: this._calendar.getWeekNumber(days.map(day => NgbDate.from(day.date)), firstDayOfWeek), days: days});
    }

    return month;
  }

  private _getFirstViewDate(date: NgbDate, firstDayOfWeek: number): NgbDate {
    const currentMonth = date.month;
    let today = new NgbDate(date.year, date.month, date.date);
    let yesterday = this._calendar.getPrevDate(today);

    const firstDayOfCurrentMonthIsAlsoFirstDayOfWeek =
        () => { return today.month !== yesterday.month && firstDayOfWeek === this._calendar.getWeekday(today); };

    const reachedTheFirstDayOfTheLastWeekOfPreviousMonth =
        () => { return today.month !== currentMonth && firstDayOfWeek === this._calendar.getWeekday(today); };

    // going back in time
    while (!reachedTheFirstDayOfTheLastWeekOfPreviousMonth() && !firstDayOfCurrentMonthIsAlsoFirstDayOfWeek()) {
      today = new NgbDate(yesterday.year, yesterday.month, yesterday.date);
      yesterday = this._calendar.getPrevDate(yesterday);
    }

    return today;
  }
}
import {NgbDate} from './ngb-date';

export type DayViewModel = {
  date: {year: number, month: number, date: number},
  disabled: boolean
}

export type WeekViewModel = {
  number: number,
  days: DayViewModel[]
}

export type MonthViewModel = {
  number: number,
  year: number,
  weeks: WeekViewModel[],
  weekdays: number[]
};

export type DayTemplateContext = {
  day: DayViewModel,
  month: MonthViewModel,
  selected: boolean
};

export enum NavigationEvent {
  PREV,
  NEXT
}
import {NgbDate} from './ngb-date';
import {Injectable} from '@angular/core';

/**
 * Default implementation of a Gregorian calendar
 */
@Injectable()
export class NgbCalendar {
  private toDate(date: NgbDate) { return new Date(date.year, date.month, date.date); }

  private fromDate(jsDate: Date) { return new NgbDate(jsDate.getFullYear(), jsDate.getMonth(), jsDate.getDate()); }

  getDaysPerWeek() { return 7; }

  getMonths() { return [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; }

  getWeeksPerMonth() { return 6; }

  getWeekday(date: NgbDate) {
    let jsDate = this.toDate(date);
    return jsDate.getDay();
  }

  getNextDate(date: NgbDate): NgbDate {
    let jsDate = this.toDate(date);
    jsDate.setDate(jsDate.getDate() + 1);
    return this.fromDate(jsDate);
  }

  getPrevDate(date: NgbDate): NgbDate {
    let jsDate = this.toDate(date);
    jsDate.setDate(jsDate.getDate() - 1);
    return this.fromDate(jsDate);
  }

  getNextMonth(date: NgbDate): NgbDate {
    let jsDate = this.toDate(date);
    jsDate = new Date(jsDate.getFullYear(), jsDate.getMonth() + 1, 1);
    return this.fromDate(jsDate);
  }

  getPrevMonth(date: NgbDate): NgbDate {
    let jsDate = this.toDate(date);
    jsDate = new Date(jsDate.getFullYear(), jsDate.getMonth() - 1, 1);
    return this.fromDate(jsDate);
  }

  getWeekNumber(week: NgbDate[], firstDayOfWeek: number) {
    const thursdayIndex = (4 + 7 - firstDayOfWeek) % 7;
    let date = week[thursdayIndex];

    const jsDate = this.toDate(date);
    jsDate.setDate(jsDate.getDate() + 4 - (jsDate.getDay() || 7));  // Thursday
    const time = jsDate.getTime();
    jsDate.setMonth(0);  // Compare with Jan 1
    jsDate.setDate(1);
    return Math.floor(Math.round((time - jsDate.getTime()) / 86400000) / 7) + 1;
  }

  getToday(): NgbDate { return this.fromDate(new Date()); }
}
export class NgbDate {
  static from(date: {year: number, month: number, date?: number}) {
    return date ? new NgbDate(date.year, date.month, date.date ? date.date : 1) : null;
  }

  constructor(public year: number, public month: number, public date: number) {}

  equals(other: NgbDate) {
    return other && this.year === other.year && this.month === other.month && this.date === other.date;
  }

  before(other: NgbDate) {
    return !other ? false : this.year === other.year ?
                    this.month === other.month ? this.date === other.date ? false : this.date < other.date :
                                                 this.month < other.month :
                    this.year < other.year;
  }

  after(other: NgbDate) {
    return !other ? false : this.year === other.year ?
                    this.month === other.month ? this.date === other.date ? false : this.date > other.date :
                                                 this.month > other.month :
                    this.year > other.year;
  }
}
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {NgbDatepicker} from './datepicker';
import {NgbDatepickerMonthView} from './datepicker-month-view';
import {NgbDatepickerNavigation} from './datepicker-navigation';
import {FormsModule} from '@angular/forms';
import {NgbDatepickerDayView} from './datepicker-day-view';
import {NgbDatepickerI18n} from './datepicker-i18n';
import {NgbCalendar} from './ngb-calendar';
import {NgbDatepickerService} from './datepicker-service';
import {NgbDatepickerNavigationSelect} from './datepicker-navigation-select';

@NgModule({
  declarations: [
    NgbDatepicker, NgbDatepickerMonthView, NgbDatepickerNavigation, NgbDatepickerNavigationSelect, NgbDatepickerDayView
  ],
  exports: [NgbDatepicker],
  imports: [CommonModule, FormsModule],
  providers: [NgbDatepickerI18n, NgbCalendar, NgbDatepickerService]
})
export class NgbDatepickerModule {
}
import {Component, Input} from '@angular/core';
import {MonthViewModel, DayViewModel} from './datepicker-view-model';

@Component({
  selector: '[ngb-datepicker-day-view]',
  styles: [`
    :host {      
      text-align: center;
      padding: 0.185rem 0.25rem;      
      border-radius: 0.25rem;
    }
  `],
  host: {'[class.bg-primary]': 'selected', '[class.text-muted]': 'day.date.month !== month.number || day.disabled'},
  template: `{{ day.date.date }}`
})
export class NgbDatepickerDayView {
  @Input() month: MonthViewModel;
  @Input() day: DayViewModel;
  @Input() selected: boolean;
}
import {Component, Input, Output, EventEmitter, OnChanges, SimpleChanges} from '@angular/core';
import {NgbDate} from './ngb-date';
import {toInteger} from '../util';
import {NgbDatepickerI18n} from './datepicker-i18n';
import {NgbCalendar} from './ngb-calendar';

@Component({
  selector: 'ngb-datepicker-navigation-select',
  styles: [`
    select {
      /* to align with btn-sm */
      padding: 0.25rem 0.5rem;
      font-size: 0.875rem;      
      line-height: 1.25;
      width: 50%;
    }
  `],
  template: `
    <select class="custom-select d-inline-block" [value]="date.month" (change)="changeMonth($event.target.value)">
      <option *ngFor="let m of months" [value]="m">{{ i18n.getMonthName(m) }}</option>
    </select>` +
      `<select class="custom-select d-inline-block" [value]="date.year" (change)="changeYear($event.target.value)">
      <option *ngFor="let y of years" [value]="y">{{ y }}</option>
    </select> 
  `  // template needs to be formatted in a certain way so we don't add empty text nodes
})
export class NgbDatepickerNavigationSelect implements OnChanges {
  months: number[];
  years: number[] = [];

  @Input() date: NgbDate;

  @Input() minYear: number;
  @Input() maxYear: number;

  @Output() select = new EventEmitter<NgbDate>();

  constructor(public i18n: NgbDatepickerI18n, calendar: NgbCalendar) { this.months = calendar.getMonths(); }

  ngOnChanges(changes: SimpleChanges) {
    if (changes['maxYear'] || changes['minYear']) {
      this._generateYears();
    }
  }

  changeMonth(month: string) { this.select.emit(new NgbDate(this.date.year, toInteger(month), 1)); }

  changeYear(year: string) { this.select.emit(new NgbDate(toInteger(year), this.date.month, 1)); }

  private _generateYears() {
    this.years = Array.from({length: this.maxYear - this.minYear + 1}, (e, i) => this.minYear + i);
  }
}
import {Injectable} from '@angular/core';

const WEEKDAYS = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];
const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];

@Injectable()
export class NgbDatepickerI18n {
  getWeekdayName(weekday: number): string { return WEEKDAYS[weekday]; }

  getMonthName(month: number): string { return MONTHS[month]; }
}