diff --git a/.gitignore b/.gitignore index 8c39e59..a041ef2 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,6 @@ typings TimeAgoPipe.js TimeAgoPipe.js.map TimeAgoPipe.d.ts +CalendarPipe.js +CalendarPipe.js.map +CalendarPipe.d.ts diff --git a/CalendarPipe.spec.ts b/CalendarPipe.spec.ts new file mode 100644 index 0000000..b022a3e --- /dev/null +++ b/CalendarPipe.spec.ts @@ -0,0 +1,42 @@ +import 'es6-shim'; +import 'es6-symbol/implement'; +import 'reflect-metadata'; +import * as moment from 'moment'; +import {CalendarPipe} from './CalendarPipe.ts'; + +describe('Calendar', () => { + + describe('CalendarPipe', () => { + var pipe:CalendarPipe; + + beforeEach(() => { + pipe = new CalendarPipe(null); + }); + + describe('#support', () => { + + it('should support Date objects', () => { + expect(pipe.supports(new Date())).toBe(true); + }); + + it('should support moment instances', () => { + expect(pipe.supports(moment())).toBe(true); + }); + + it('should not support string', () => { + expect(pipe.supports('foobar')).toBe(false); + }); + + it('should not support null', () => { + expect(pipe.supports(null)).toBe(false); + }); + + }); + + describe('#transform', () => { + it('should transform the start of the current day to "Today at 12:00 AM"', () => { + expect(pipe.transform(moment().startOf('day'))).toBe('Today at 12:00 AM'); + }); + }); + }); +}); diff --git a/CalendarPipe.ts b/CalendarPipe.ts new file mode 100644 index 0000000..44acdb6 --- /dev/null +++ b/CalendarPipe.ts @@ -0,0 +1,80 @@ +/* angular2-moment / v0.1.1 / (c) 2015 Uri Shaked / MIT Licence */ + +import {Pipe, ChangeDetectorRef, PipeTransform, EventEmitter} from 'angular2/core'; +import * as moment_ from 'moment'; + +// under systemjs, moment is actually exported as the default export, so we account for that +const moment:moment.MomentStatic = (moment_)['default'] || moment_; + +@Pipe({name: 'amCalendar', pure: false}) +export class CalendarPipe implements PipeTransform { + + /** + * @private Internal reference counter, so we can clean up when no instances are in use + * @type {number} + */ + private static _refs: number = 0; + + private static _timer: number; + private static _midnight: EventEmitter; + + constructor(private _cdRef:ChangeDetectorRef) { + // using a single static timer for all instances of this pipe for performance reasons + CalendarPipe._initTimer(); + + CalendarPipe._refs++; + + // values such as Today will need to be replaced with Yesterday after midnight, + // so make sure we subscribe to an EventEmitter that we set up to emit at midnight + CalendarPipe._midnight.subscribe(() => this._cdRef.markForCheck()); + } + + supports(value:any):boolean { + return value instanceof Date || moment.isMoment(value); + } + + transform(value:Date | moment.Moment, args?:any[]):any { + return moment(value).calendar(); + } + + onDestroy():void { + if (CalendarPipe._refs > 0) { + CalendarPipe._refs--; + } + + if (CalendarPipe._refs === 0) { + CalendarPipe._removeTimer(); + } + } + + private static _initTimer() { + // initialize the timer + if (!CalendarPipe._midnight) { + CalendarPipe._midnight = new EventEmitter(); + let timeToUpdate = CalendarPipe._getMillisecondsUntilUpdate(); + CalendarPipe._timer = window.setTimeout(() => { + // emit the current date + CalendarPipe._midnight.emit(new Date()); + + // refresh the timer + CalendarPipe._removeTimer(); + CalendarPipe._initTimer(); + }, timeToUpdate); + } + } + + private static _removeTimer() { + if (CalendarPipe._timer) { + window.clearTimeout(CalendarPipe._timer); + CalendarPipe._timer = null; + CalendarPipe._midnight = null; + } + } + + private static _getMillisecondsUntilUpdate() { + var now = moment(); + var tomorrow = moment().startOf('day').add(1, 'days'); + var timeToMidnight = tomorrow.valueOf() - now.valueOf(); + return timeToMidnight + 1000; // 1 second after midnight + } +} diff --git a/index.ts b/index.ts new file mode 100644 index 0000000..9bb8ec2 --- /dev/null +++ b/index.ts @@ -0,0 +1,2 @@ +export * from './TimeAgoPipe'; +export * from './CalendarPipe'; diff --git a/package.json b/package.json index ba79148..c6fe672 100644 --- a/package.json +++ b/package.json @@ -2,13 +2,17 @@ "name": "angular2-moment", "version": "0.1.1", "description": "Moment.JS pipes for Angular2 (timeago and more)", - "main": "TimeAgoPipe.js", - "typings": "./TimeAgoPipe.d.ts", + "main": "index.js", + "typings": "./index.d.ts", "files": [ "TimeAgoPipe.js", "TimeAgoPipe.js.map", "TimeAgoPipe.d.ts", "TimeAgoPipe.ts", + "CalendarPipe.js", + "CalendarPipe.js.map", + "CalendarPipe.d.ts", + "CalendarPipe.ts", "typings", "CHANGELOG.md" ], diff --git a/tsconfig.json b/tsconfig.json index 864776f..d3caaa2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,7 +10,9 @@ }, "files": [ "typings/moment/moment.d.ts", - "TimeAgoPipe.ts" + "TimeAgoPipe.ts", + "CalendarPipe.ts", + "index.ts" ], "exclude": [ "node_modules"