diff --git a/demo/src/ng-api-doc.ts b/demo/src/ng-api-doc.ts index ec6d736028..742949b93f 100644 --- a/demo/src/ng-api-doc.ts +++ b/demo/src/ng-api-doc.ts @@ -139,12 +139,19 @@ export const ngdoc: any = { "properties": [] }, "LocaleData": { - "fileName": "src/datepicker/models/index.ts", + "fileName": "src/bs-moment/locale/locale.class.ts", "className": "LocaleData", "description": "", "methods": [], "properties": [] }, + "TimeUnit": { + "fileName": "src/bs-moment/types.ts", + "className": "TimeUnit", + "description": "", + "methods": [], + "properties": [] + }, "ButtonCheckboxDirective": { "fileName": "src/buttons/button-checkbox.directive.ts", "className": "ButtonCheckboxDirective", @@ -489,9 +496,14 @@ export const ngdoc: any = { "fileName": "src/datepicker/bs-datepicker.component.ts", "className": "BsDatepickerComponent", "description": "", - "selector": "bs-datepicker", + "selector": "bs-datepicker,[bsDatepicker]", "exportAs": "bsDatepicker", "inputs": [ + { + "name": "bsValue", + "type": "Date", + "description": "" + }, { "name": "container", "defaultValue": "body", @@ -503,6 +515,12 @@ export const ngdoc: any = { "type": "boolean", "description": "
Returns whether or not the popover is currently being shown
\n" }, + { + "name": "outsideClick", + "defaultValue": "true", + "type": "boolean", + "description": "" + }, { "name": "placement", "defaultValue": "bottom", @@ -514,14 +532,13 @@ export const ngdoc: any = { "defaultValue": "click", "type": "string", "description": "Specifies events that should trigger. Supports a space separated list of\nevent names.
\n" - }, - { - "name": "value", - "type": "Date", - "description": "" } ], "outputs": [ + { + "name": "bsValueChange", + "description": "" + }, { "name": "onHidden", "description": "Emits an event when the popover is hidden
\n" @@ -529,10 +546,6 @@ export const ngdoc: any = { { "name": "onShown", "description": "Emits an event when the popover is shown
\n" - }, - { - "name": "valueChange", - "description": "" } ], "properties": [], @@ -561,8 +574,14 @@ export const ngdoc: any = { "fileName": "src/datepicker/bs-daterangepicker.component.ts", "className": "BsDaterangepickerComponent", "description": "", - "selector": "bs-daterangepicker", + "selector": "bs-daterangepicker,[bsDaterangepicker]", + "exportAs": "bsDaterangepicker", "inputs": [ + { + "name": "bsValue", + "type": "Date[]", + "description": "" + }, { "name": "container", "defaultValue": "body", @@ -574,6 +593,12 @@ export const ngdoc: any = { "type": "boolean", "description": "Returns whether or not the popover is currently being shown
\n" }, + { + "name": "outsideClick", + "defaultValue": "true", + "type": "boolean", + "description": "" + }, { "name": "placement", "defaultValue": "bottom", @@ -585,14 +610,13 @@ export const ngdoc: any = { "defaultValue": "click", "type": "string", "description": "Specifies events that should trigger. Supports a space separated list of\nevent names.
\n" - }, - { - "name": "value", - "type": "Date[]", - "description": "" } ], "outputs": [ + { + "name": "bsValueChange", + "description": "" + }, { "name": "onHidden", "description": "Emits an event when the popover is hidden
\n" @@ -600,10 +624,6 @@ export const ngdoc: any = { { "name": "onShown", "description": "Emits an event when the popover is shown
\n" - }, - { - "name": "valueChange", - "description": "" } ], "properties": [], @@ -967,13 +987,6 @@ export const ngdoc: any = { "methods": [], "properties": [] }, - "TimeUnit": { - "fileName": "src/datepicker/models/index.ts", - "className": "TimeUnit", - "description": "", - "methods": [], - "properties": [] - }, "BsNavigationEvent": { "fileName": "src/datepicker/models/index.ts", "className": "BsNavigationEvent", @@ -1040,11 +1053,11 @@ export const ngdoc: any = { "properties": [], "methods": [] }, - "BsDatepickerDayViewComponent": { - "fileName": "src/datepicker/themes/bs/bs-datepicker-day-view.component.ts", - "className": "BsDatepickerDayViewComponent", + "BsDatepickerDayDecoratorComponent": { + "fileName": "src/datepicker/themes/bs/bs-datepicker-day-decorator.directive.ts", + "className": "BsDatepickerDayDecoratorComponent", "description": "", - "selector": "bs-datepicker-day-view", + "selector": "[bsDatepickerDayDecorator]", "inputs": [ { "name": "day", @@ -1052,16 +1065,7 @@ export const ngdoc: any = { "description": "" } ], - "outputs": [ - { - "name": "onHover", - "description": "" - }, - { - "name": "onSelect", - "description": "" - } - ], + "outputs": [], "properties": [], "methods": [] }, diff --git a/package.json b/package.json index aac19421b3..0dc93af7a5 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "demo.docs": "node scripts/docs/get-doc.js", "demo.serve": "run-s build link demo.docs demo.build lite-server", "demo.gh-pages": "run-s build demo.docs demo.build demo.deploy", - "demo.build": "ng build -prod --aot && npm run generate-bs4", + "demo.build": "ng build -prod --aot --build-optimizer && npm run generate-bs4", "demo.deploy": "gh-pages -d demo/dist", "demo-beta-deploy": "gh-pages -d demo/dist -r git@github.com:valorkin/ngx-bootstrap.git -b gh-pages", "link": "ngm link -p src --here", @@ -55,8 +55,7 @@ "url": "https://github.com/valor-software/ngx-bootstrap/issues" }, "homepage": "https://github.com/valor-software/ngx-bootstrap#readme", - "dependencies": { - }, + "dependencies": {}, "peerDependencies": { "@angular/common": "^2.3.1 || >=4.0.0", "@angular/compiler": "^2.3.1 || >=4.0.0", diff --git a/src/bs-moment/format-functions.ts b/src/bs-moment/format-functions.ts index 697b33e1dc..db9db9df32 100644 --- a/src/bs-moment/format-functions.ts +++ b/src/bs-moment/format-functions.ts @@ -1,9 +1,9 @@ import { Locale } from './locale/locale.class'; -import { DateFormatterFn } from '../datepicker/models/index'; import { zeroFill } from './utils'; import { isFunction } from './utils/type-checks'; +import { DateFormatterFn } from '../datepicker/models/index'; -export let formatFunctions: {[key:string]: (date: Date, locale: Locale) => string} = {}; +export let formatFunctions: { [key: string]: (date: Date, locale: Locale) => string } = {}; export let formatTokenFunctions: { [key: string]: DateFormatterFn } = {}; export const formattingTokens = /(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g; @@ -13,14 +13,14 @@ export const formattingTokens = /(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DD // ordinal: 'Mo' // callback: function () { this.month() + 1 } export function addFormatToken(token: string, - padded: {[key: number]: any}, + padded: { [key: number]: any }, ordinal: string, callback: DateFormatterFn): void { let func: DateFormatterFn = callback; if (token) { formatTokenFunctions[token] = func; } - if (padded as {[key: number]: any}) { + if (padded as { [key: number]: any }) { let key = padded[0] as string; formatTokenFunctions[key] = function (date: Date, format: string, locale?: Locale): string { return zeroFill(func.apply(null, arguments), padded[1] as number, padded[2] as boolean); diff --git a/src/bs-moment/format.ts b/src/bs-moment/format.ts index a39b17ad6a..8a6c8221d2 100644 --- a/src/bs-moment/format.ts +++ b/src/bs-moment/format.ts @@ -6,9 +6,9 @@ import { formatFunctions, makeFormatFunction } from './format-functions'; import './locale'; +import './units'; import { Locale } from './locale/locale.class'; import { getLocale } from './locale/locales.service'; -import './units'; import { isDateValid } from './utils/type-checks'; export function formatDate(date: Date, format: string, locale = 'en'): string { diff --git a/src/bs-moment/i18n/ar.ts b/src/bs-moment/i18n/ar.ts index 40a92af378..550ff2bc93 100644 --- a/src/bs-moment/i18n/ar.ts +++ b/src/bs-moment/i18n/ar.ts @@ -4,6 +4,8 @@ // author : Ahmed Elkhatib // author : forabi https://github.com/forabi +import { LocaleData } from '../locale/locale.class'; + const symbolMap: { [key: string]: string } = { '1': '١', '2': '٢', @@ -31,7 +33,7 @@ const numberMap: { [key: string]: string } = { const pluralForm = function (n: number): number { return n === 0 ? 0 : n === 1 ? 1 : n === 2 ? 2 : n % 100 >= 3 && n % 100 <= 10 ? 3 : n % 100 >= 11 ? 4 : 5; }; -const plurals = { +const plurals: any = { s: ['أقل من ثانية', 'ثانية واحدة', ['ثانيتان', 'ثانيتين'], '%d ثوان', '%d ثانية', '%d ثانية'], m: ['أقل من دقيقة', 'دقيقة واحدة', ['دقيقتان', 'دقيقتين'], '%d دقائق', '%d دقيقة', '%d دقيقة'], h: ['أقل من ساعة', 'ساعة واحدة', ['ساعتان', 'ساعتين'], '%d ساعات', '%d ساعة', '%d ساعة'], @@ -39,14 +41,15 @@ const plurals = { M: ['أقل من شهر', 'شهر واحد', ['شهران', 'شهرين'], '%d أشهر', '%d شهرا', '%d شهر'], y: ['أقل من عام', 'عام واحد', ['عامان', 'عامين'], '%d أعوام', '%d عامًا', '%d عام'] }; -const pluralize = function (u) { - return function (number, withoutSuffix, string, isFuture) { - const f = pluralForm(number); - let str = plurals[u][pluralForm(number)]; +const pluralize = function (u: string) { + return function (num: number, withoutSuffix: boolean/*, string, isFuture*/) { + const f = pluralForm(num); + let str = plurals[u][pluralForm(num)]; if (f === 2) { str = str[withoutSuffix ? 0 : 1]; } - return str.replace(/%d/i, number); + + return str.replace(/%d/i, num); }; }; const months = [ @@ -64,9 +67,9 @@ const months = [ 'كانون الأول ديسمبر' ]; -export const arLocale = { +export const ar: LocaleData = { abbr: 'ar', - months: months, + months, monthsShort: months, weekdays: 'الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت'.split('_'), weekdaysShort: 'أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت'.split('_'), @@ -81,10 +84,10 @@ export const arLocale = { LLLL: 'dddd D MMMM YYYY HH:mm' }, meridiemParse: /ص|م/, - isPM: function (input) { - return 'م' === input; + isPM(input: string): boolean { + return input === 'م'; }, - meridiem: function (hour, minute, isLower) { + meridiem(hour: number, minute: number, isLower: boolean): string { if (hour < 12) { return 'ص'; } else { @@ -114,13 +117,13 @@ export const arLocale = { y: pluralize('y'), yy: pluralize('y') }, - preparse: function (string) { - return string.replace(/[١٢٣٤٥٦٧٨٩٠]/g, function (match) { + preparse(str: string): string { + return str.replace(/[١٢٣٤٥٦٧٨٩٠]/g, function (match) { return numberMap[match]; }).replace(/،/g, ','); }, - postformat: function (string) { - return string.replace(/\d/g, function (match) { + postformat(str: string): string { + return str.replace(/\d/g, function (match) { return symbolMap[match]; }).replace(/,/g, '،'); }, diff --git a/src/bs-moment/locale/locale.class.ts b/src/bs-moment/locale/locale.class.ts index f4cebeb60e..fc4c223cc7 100644 --- a/src/bs-moment/locale/locale.class.ts +++ b/src/bs-moment/locale/locale.class.ts @@ -31,8 +31,10 @@ export interface LocaleData { week?: { dow: number, doy: number }; dayOfMonthOrdinalParse?: RegExp; - ordinal?: (num: number) => string; - postformat?: (num: string) => string; + meridiemParse?: RegExp; + + ordinal?(num: number, token?: string): string; + postformat?(num: string): string; } export class Locale { @@ -57,7 +59,7 @@ export class Locale { } } - set (config: LocaleData): void { + set(config: LocaleData): void { for (const i in config) { if (!config.hasOwnProperty(i)) { continue; @@ -99,7 +101,7 @@ export class Locale { return (this._monthsShort as string[])[getMonth(date)]; } let key = MONTHS_IN_FORMAT.test(format) ? 'format' : 'standalone'; - return ((this._monthsShort as any)[key] as string[])[getMonth(date)]; + return ((this._monthsShort as any)[key] as string[])[getMonth(date)]; } // Days of week @@ -141,6 +143,14 @@ export class Locale { return this._week.doy; } + meridiem(hours: number, minutes: number, isLower: boolean): string { + if (hours > 11) { + return isLower ? 'pm' : 'PM'; + } + + return isLower ? 'am' : 'AM'; + } + ordinal(num: number, token?: string): string { return this._ordinal.replace('%d', num.toString(10)); } diff --git a/src/bs-moment/locale/locale.defaults.ts b/src/bs-moment/locale/locale.defaults.ts index 27e72143dd..9990a002fb 100644 --- a/src/bs-moment/locale/locale.defaults.ts +++ b/src/bs-moment/locale/locale.defaults.ts @@ -11,6 +11,8 @@ export const defaultLocaleWeek = { doy : 6 // The week that contains Jan 1st is the first week of the year. }; +export const defaultLocaleMeridiemParse = /[ap]\.?m?\.?/i; + export const baseConfig: LocaleData = { // calendar: defaultCalendar, // longDateFormat: defaultLongDateFormat, @@ -28,5 +30,5 @@ export const baseConfig: LocaleData = { weekdaysMin: defaultLocaleWeekdaysMin, weekdaysShort: defaultLocaleWeekdaysShort, - // meridiemParse: defaultLocaleMeridiemParse + meridiemParse: defaultLocaleMeridiemParse }; diff --git a/src/bs-moment/test/locale/en.spec.ts b/src/bs-moment/test/locale/en.spec.ts new file mode 100644 index 0000000000..21f07340d5 --- /dev/null +++ b/src/bs-moment/test/locale/en.spec.ts @@ -0,0 +1,141 @@ +// import { getLocale } from '../locale/locales.service'; +import { formatDate } from '../../format'; + +const localeAbbr = 'en'; +// const locale = getLocale(localeAbbr); + +describe('moment - locale: en', () => { + it('format', () => { + const expected: string[][] = [ + ['dddd, MMMM Do YYYY, h:mm:ss a', 'Sunday, February 14th 2010, 3:25:50 pm'], + ['ddd, hA', 'Sun, 3PM'], + ['M Mo MM MMMM MMM', '2 2nd 02 February Feb'], + ['YYYY YY', '2010 10'], + ['D Do DD', '14 14th 14'], + ['d do dddd ddd dd', '0 0th Sunday Sun Su'], + ['DDD DDDo DDDD', '45 45th 045'], + ['w wo ww', '8 8th 08'], + ['h hh', '3 03'], + ['H HH', '15 15'], + ['m mm', '25 25'], + ['s ss', '50 50'], + ['a A', 'pm PM'], + ['[the] DDDo [day of the year]', 'the 45th day of the year'] + // ['LTS', '3:25:50 PM'], + // ['L', '02/14/2010'], + // ['LL', 'February 14, 2010'], + // ['LLL', 'February 14, 2010 3:25 PM'], + // ['LLLL', 'Sunday, February 14, 2010 3:25 PM'], + // ['l', '2/14/2010'], + // ['ll', 'Feb 14, 2010'], + // ['lll', 'Feb 14, 2010 3:25 PM'], + // ['llll', 'Sun, Feb 14, 2010 3:25 PM'] + ]; + const date = new Date(2010, 1, 14, 15, 25, 50, 125); + + for (let i = 0; i < expected.length; i++) { + expect(formatDate(date, expected[i][0], localeAbbr)) + .toBe(expected[i][1], `${expected[i][0]} ---> ${expected[i][1]}`); + } + }); + + it('format ordinal', () => { + expect(formatDate(new Date(2011, 0, 1), 'DDDo', localeAbbr)) + .toBe('1st', '1st'); + expect(formatDate(new Date(2011, 0, 2), 'DDDo', localeAbbr)) + .toBe('2nd', '2nd'); + expect(formatDate(new Date(2011, 0, 3), 'DDDo', localeAbbr)) + .toBe('3rd', '3rd'); + expect(formatDate(new Date(2011, 0, 4), 'DDDo', localeAbbr)) + .toBe('4th', '4th'); + expect(formatDate(new Date(2011, 0, 5), 'DDDo', localeAbbr)) + .toBe('5th', '5th'); + expect(formatDate(new Date(2011, 0, 6), 'DDDo', localeAbbr)) + .toBe('6th', '6th'); + expect(formatDate(new Date(2011, 0, 7), 'DDDo', localeAbbr)) + .toBe('7th', '7th'); + expect(formatDate(new Date(2011, 0, 8), 'DDDo', localeAbbr)) + .toBe('8th', '8th'); + expect(formatDate(new Date(2011, 0, 9), 'DDDo', localeAbbr)) + .toBe('9th', '9th'); + expect(formatDate(new Date(2011, 0, 10), 'DDDo', localeAbbr)) + .toBe('10th', '10th'); + + expect(formatDate(new Date(2011, 0, 11), 'DDDo', localeAbbr)) + .toBe('11th', '11th'); + expect(formatDate(new Date(2011, 0, 12), 'DDDo', localeAbbr)) + .toBe('12th', '12th'); + expect(formatDate(new Date(2011, 0, 13), 'DDDo', localeAbbr)) + .toBe('13th', '13th'); + expect(formatDate(new Date(2011, 0, 14), 'DDDo', localeAbbr)) + .toBe('14th', '14th'); + expect(formatDate(new Date(2011, 0, 15), 'DDDo', localeAbbr)) + .toBe('15th', '15th'); + expect(formatDate(new Date(2011, 0, 16), 'DDDo', localeAbbr)) + .toBe('16th', '16th'); + expect(formatDate(new Date(2011, 0, 17), 'DDDo', localeAbbr)) + .toBe('17th', '17th'); + expect(formatDate(new Date(2011, 0, 18), 'DDDo', localeAbbr)) + .toBe('18th', '18th'); + expect(formatDate(new Date(2011, 0, 19), 'DDDo', localeAbbr)) + .toBe('19th', '19th'); + expect(formatDate(new Date(2011, 0, 20), 'DDDo', localeAbbr)) + .toBe('20th', '20th'); + + expect(formatDate(new Date(2011, 0, 21), 'DDDo', localeAbbr)) + .toBe('21st', '21st'); + expect(formatDate(new Date(2011, 0, 22), 'DDDo', localeAbbr)) + .toBe('22nd', '22nd'); + expect(formatDate(new Date(2011, 0, 23), 'DDDo', localeAbbr)) + .toBe('23rd', '23rd'); + expect(formatDate(new Date(2011, 0, 24), 'DDDo', localeAbbr)) + .toBe('24th', '24th'); + expect(formatDate(new Date(2011, 0, 25), 'DDDo', localeAbbr)) + .toBe('25th', '25th'); + expect(formatDate(new Date(2011, 0, 26), 'DDDo', localeAbbr)) + .toBe('26th', '26th'); + expect(formatDate(new Date(2011, 0, 27), 'DDDo', localeAbbr)) + .toBe('27th', '27th'); + expect(formatDate(new Date(2011, 0, 28), 'DDDo', localeAbbr)) + .toBe('28th', '28th'); + expect(formatDate(new Date(2011, 0, 29), 'DDDo', localeAbbr)) + .toBe('29th', '29th'); + expect(formatDate(new Date(2011, 0, 30), 'DDDo', localeAbbr)) + .toBe('30th', '30th'); + + expect(formatDate(new Date(2011, 0, 31), 'DDDo', localeAbbr)) + .toBe('31st', '31st'); + }); + + it('format month', () => { + const expected = 'January Jan_February Feb_March Mar_April Apr_May May_June Jun_July Jul_August Aug_September Sep_October Oct_November Nov_December Dec'.split('_'); + + for (let i = 0; i < expected.length; i++) { + expect(formatDate(new Date(2011, i, 1), 'MMMM MMM', localeAbbr)) + .toBe(expected[i], expected[i]); + } + }); + + it('format week', () => { + const expected = 'Sunday Sun Su_Monday Mon Mo_Tuesday Tue Tu_Wednesday Wed We_Thursday Thu Th_Friday Fri Fr_Saturday Sat Sa'.split('_'); + + for (let i = 0; i < expected.length; i++) { + expect(formatDate(new Date(2011, 0, i + 2), 'dddd ddd dd', localeAbbr)) + .toBe(expected[i], expected[i]); + } + }); + + it('weeks year starting sunday format', () => { + expect(formatDate(new Date(2012, 0, 1), 'w ww wo', localeAbbr)) + .toBe('1 01 1st', 'Jan 1 2012 should be week 1'); + expect(formatDate(new Date(2012, 0, 7), 'w ww wo', localeAbbr)) + .toBe('1 01 1st', 'Jan 7 2012 should be week 1'); + expect(formatDate(new Date(2012, 0, 8), 'w ww wo', localeAbbr)) + .toBe('2 02 2nd', 'Jan 8 2012 should be week 2'); + expect(formatDate(new Date(2012, 0, 14), 'w ww wo', localeAbbr)) + .toBe('2 02 2nd', 'Jan 14 2012 should be week 2'); + expect(formatDate(new Date(2012, 0, 15), 'w ww wo', localeAbbr)) + .toBe('3 03 3rd', 'Jan 15 2012 should be week 3'); + }); + +}); diff --git a/src/bs-moment/test/units.spec.ts b/src/bs-moment/test/units.spec.ts new file mode 100644 index 0000000000..2d788a3abd --- /dev/null +++ b/src/bs-moment/test/units.spec.ts @@ -0,0 +1,14 @@ +import { getDayOfYear } from '../units/day-of-year'; + +describe('moment - units', () => { + it('day of year', () => { + expect(getDayOfYear(new Date(2000, 0, 1))).toBe(1, 'Jan 1 2000 should be day 1 of the year'); + expect(getDayOfYear(new Date(2000, 1, 28))).toBe(59, 'Feb 28 2000 should be day 59 of the year'); + expect(getDayOfYear(new Date(2000, 1, 29))).toBe(60, 'Feb 28 2000 should be day 60 of the year'); + expect(getDayOfYear(new Date(2000, 11, 31))).toBe(366, 'Dec 31 2000 should be day 366 of the year'); + expect(getDayOfYear(new Date(2001, 0, 1))).toBe(1, 'Jan 1 2001 should be day 1 of the year'); + expect(getDayOfYear(new Date(2001, 1, 28))).toBe(59, 'Feb 28 2001 should be day 59 of the year'); + expect(getDayOfYear(new Date(2001, 2, 1))).toBe(60, 'Mar 1 2001 should be day 60 of the year'); + expect(getDayOfYear(new Date(2001, 11, 31))).toBe(365, 'Dec 31 2001 should be day 365 of the year'); + }); +}); diff --git a/src/bs-moment/types.ts b/src/bs-moment/types.ts new file mode 100644 index 0000000000..4d4975ca34 --- /dev/null +++ b/src/bs-moment/types.ts @@ -0,0 +1,11 @@ +export type UnitOfTime = 'year' | 'month' | 'week' | 'day' | 'hour' | + 'minute' | 'seconds' | 'milliseconds'; + +export interface TimeUnit { + year?: number; + month?: number; + day?: number; + hour?: number; + minute?: number; + seconds?: number; +} diff --git a/src/bs-moment/units/day-of-week.ts b/src/bs-moment/units/day-of-week.ts index 6e9566e63c..b57c6178f4 100644 --- a/src/bs-moment/units/day-of-week.ts +++ b/src/bs-moment/units/day-of-week.ts @@ -8,11 +8,11 @@ addFormatToken('d', null, 'do', function (date: Date): string { }); addFormatToken('dd', null, null, function (date: Date, format: string, locale?: Locale): string { - return locale.weekdaysMin(date) as string; + return locale.weekdaysShort(date) as string; }); addFormatToken('ddd', null, null, function (date: Date, format: string, locale?: Locale): string { - return locale.weekdaysShort(date) as string; + return locale.weekdaysMin(date) as string; }); addFormatToken('dddd', null, null, function (date: Date, format: string, locale?: Locale): string { diff --git a/src/bs-moment/units/day-of-year.ts b/src/bs-moment/units/day-of-year.ts index e344317317..d17e7b669c 100644 --- a/src/bs-moment/units/day-of-year.ts +++ b/src/bs-moment/units/day-of-year.ts @@ -1,13 +1,24 @@ import { addFormatToken } from '../format-functions'; +import { startOf } from '../utils/start-end-of'; // FORMATTING addFormatToken('DDD', ['DDDD', 3], 'DDDo', function (date: Date): string { return getDayOfYear(date).toString(10); }); -export function getDayOfYear(date: Date) { +export function getDayOfYear(date: Date): number { + const date1 = +startOf(date, 'day'); + const date2 = +startOf(date, 'year'); + const someDate = date1 - date2; + const oneDay = 1000 * 60 * 60 * 24; + + return Math.round(someDate / oneDay) + 1; +} + +export function _getDayOfYear(date: Date): number { const start = new Date(date.getFullYear(), 0, 0); const diff = date.getTime() - start.getTime(); const oneDay = 1000 * 60 * 60 * 24; - return Math.round(diff / oneDay); + + return Math.round(diff / oneDay) + 1; } diff --git a/src/bs-moment/units/hour.ts b/src/bs-moment/units/hour.ts new file mode 100644 index 0000000000..f1d2de3d8d --- /dev/null +++ b/src/bs-moment/units/hour.ts @@ -0,0 +1,60 @@ +// import { makeGetSet } from '../moment/get-set'; +// import { addFormatToken } from '../format/format'; +// import { addUnitAlias } from './aliases'; +// import { addUnitPriority } from './priorities'; +// import { addRegexToken, match1to2, match2, match3to4, match5to6 } from '../parse/regex'; +// import { addParseToken } from '../parse/token'; +// import { HOUR, MINUTE, SECOND } from './constants'; +// import toInt from '../utils/to-int'; +// import zeroFill from '../utils/zero-fill'; +// import getParsingFlags from '../create/parsing-flags'; + +// FORMATTING + +import { getHours, getMinutes, getSeconds } from '../utils/date-getters'; +import { addFormatToken } from '../format-functions'; +import { zeroFill } from '../utils'; +import { Locale } from '../locale/locale.class'; + +function hFormat(date: Date): number { + return getHours(date) % 12 || 12; +} + +function kFormat(date: Date): number { + return getHours(date) || 24; +} + +addFormatToken('H', ['HH', 2], null, function (date: Date, format: string, locale?: Locale): string { + return getHours(date).toString(10); +}); +addFormatToken('h', ['hh', 2], null, function (date: Date, format: string, locale?: Locale): string { + return hFormat(date).toString(10); +}); +addFormatToken('k', ['kk', 2], null, function (date: Date, format: string, locale?: Locale): string { + return kFormat(date).toString(10); +}); + +addFormatToken('hmm', null, null, function (date: Date, format: string, locale?: Locale): string { + return `${hFormat(date)}${zeroFill(getMinutes(date), 2)}`; +}); + +addFormatToken('hmmss', null, null, function (date: Date, format: string, locale?: Locale): string { + return `${hFormat(date)}${zeroFill(getMinutes(date), 2)}${zeroFill(getSeconds(date), 2)}`; +}); + +addFormatToken('Hmm', null, null, function (date: Date, format: string, locale?: Locale): string { + return `${getHours(date)}${zeroFill(getMinutes(date), 2)}`; +}); + +addFormatToken('Hmmss', null, null, function (date: Date, format: string, locale?: Locale): string { + return `${getHours(date)}${zeroFill(getMinutes(date), 2)}${zeroFill(getSeconds(date), 2)}`; +}); + +function meridiem(token: string, lowercase: boolean): void { + addFormatToken(token, null, null, function (date: Date, format: string, locale?: Locale): string { + return locale.meridiem(getHours(date), getMinutes(date), lowercase); + }); +} + +meridiem('a', true); +meridiem('A', false); diff --git a/src/bs-moment/units/index.ts b/src/bs-moment/units/index.ts index b72bd38374..e46daf2cd6 100644 --- a/src/bs-moment/units/index.ts +++ b/src/bs-moment/units/index.ts @@ -1,7 +1,10 @@ import './day-of-month'; import './day-of-week'; import './day-of-year'; +import './hour'; +import './minute'; import './month'; +import './second'; import './week'; import './week-calendar-utils'; import './year'; diff --git a/src/bs-moment/units/minute.ts b/src/bs-moment/units/minute.ts new file mode 100644 index 0000000000..77e8e00c08 --- /dev/null +++ b/src/bs-moment/units/minute.ts @@ -0,0 +1,6 @@ +import { addFormatToken } from '../format-functions'; +import { getMinutes } from '../utils/date-getters'; + +addFormatToken('m', ['mm', 2], null, function (date: Date) { + return getMinutes(date).toString(10); +}); diff --git a/src/bs-moment/units/month.ts b/src/bs-moment/units/month.ts index 8e698edc03..135f83820c 100644 --- a/src/bs-moment/units/month.ts +++ b/src/bs-moment/units/month.ts @@ -10,6 +10,7 @@ export function daysInMonth(year: number, month: number): number { } const modMonth = mod(month, 12); year += (month - modMonth) / 12; + return modMonth === 1 ? (isLeapYear(year) ? 29 : 28) : (31 - modMonth % 7 % 2); } diff --git a/src/bs-moment/units/second.ts b/src/bs-moment/units/second.ts new file mode 100644 index 0000000000..8add2ee93d --- /dev/null +++ b/src/bs-moment/units/second.ts @@ -0,0 +1,7 @@ +import { addFormatToken } from '../format-functions'; +import { getSeconds } from '../utils/date-getters'; + +addFormatToken('s', ['ss', 2], null, function (date: Date): string { + return getSeconds(date).toString(10); +}); + diff --git a/src/bs-moment/utils.ts b/src/bs-moment/utils.ts index 5c76a21613..8c6a2a2993 100644 --- a/src/bs-moment/utils.ts +++ b/src/bs-moment/utils.ts @@ -1,7 +1,7 @@ -export function zeroFill(number: number, targetLength: number, forceSign: boolean): string { - const absNumber = '' + Math.abs(number); +export function zeroFill(num: number, targetLength: number, forceSign?: boolean): string { + const absNumber = `${Math.abs(num)}`; const zerosToFill = targetLength - absNumber.length; - const sign = number >= 0; + const sign = num >= 0; return (sign ? (forceSign ? '+' : '') : '-') + Math.pow(10, Math.max(0, zerosToFill)).toString().substr(1) + absNumber; } diff --git a/src/bs-moment/utils/date-getters.ts b/src/bs-moment/utils/date-getters.ts index 339e244350..7b56026f3a 100644 --- a/src/bs-moment/utils/date-getters.ts +++ b/src/bs-moment/utils/date-getters.ts @@ -1,8 +1,22 @@ -import { createDate } from '../../datepicker/utils/date-utils'; + +import { createDate } from './date-setters'; + +export function getHours(date: Date, isUTC = false): number { + return isUTC ? date.getUTCHours() : date.getHours(); +} + +export function getMinutes(date: Date, isUTC = false): number { + return isUTC ? date.getUTCMinutes() : date.getMinutes(); +} + +export function getSeconds(date: Date, isUTC = false): number { + return isUTC ? date.getUTCSeconds() : date.getSeconds(); +} export function getDayOfWeek(date: Date, isUTC = false): number { return isUTC ? date.getUTCDay() : date.getDay(); } + export function getDate(date: Date, isUTC = false): number { return isUTC ? date.getUTCDate() : date.getDate(); } diff --git a/src/datepicker/utils/date-utils.ts b/src/bs-moment/utils/date-setters.ts similarity index 53% rename from src/datepicker/utils/date-utils.ts rename to src/bs-moment/utils/date-setters.ts index fba50508f4..d7c93ca825 100644 --- a/src/datepicker/utils/date-utils.ts +++ b/src/bs-moment/utils/date-setters.ts @@ -1,4 +1,4 @@ -import { TimeUnit } from '../models/index'; +import { TimeUnit } from '../types'; const defaultTimeUnit: TimeUnit = { year: 0, month: 0, day: 0, hour: 0, minute: 0, seconds: 0 @@ -9,8 +9,9 @@ export function createDate(year?: number, month = 0, day = 1, hour = 0, minute = return new Date(year || _date.getFullYear(), month, day, hour, minute, seconds); } -export function changeDate(date: Date, unit: TimeUnit): Date { +export function shiftDate(date: Date, unit: TimeUnit): Date { const _unit = Object.assign({}, defaultTimeUnit, unit); + return createDate(date.getFullYear() + _unit.year, date.getMonth() + _unit.month, date.getDate() + _unit.day, @@ -19,3 +20,18 @@ export function changeDate(date: Date, unit: TimeUnit): Date { date.getSeconds() + _unit.seconds ); } + +export function setDate(date: Date, unit: TimeUnit): Date { + return createDate( + getNum(date.getFullYear(), unit.year), + getNum(date.getMonth(), unit.month), + getNum(date.getDate(), unit.day), + getNum(date.getHours(), unit.hour), + getNum(date.getMinutes(), unit.minute), + getNum(date.getSeconds(), unit.seconds) + ); +} + +function getNum( def: number, num: number): number { + return typeof num === 'number' ? num : def; +} diff --git a/src/bs-moment/utils/start-end-of.ts b/src/bs-moment/utils/start-end-of.ts new file mode 100644 index 0000000000..275d21975c --- /dev/null +++ b/src/bs-moment/utils/start-end-of.ts @@ -0,0 +1,42 @@ +import { TimeUnit, UnitOfTime } from '../types'; +import { setDate, shiftDate } from './date-setters'; + +export function startOf(date: Date, units: UnitOfTime): Date { + const unit = getDateShift(units); + return setDate(date, unit); +} + +export function endOf(date: Date, units: UnitOfTime): Date { + const start = startOf(date, units); + const shift = {[units]: 1}; + const change = shiftDate(start, shift); + change.setMilliseconds(-1); + + return change; +} + +function getDateShift(units: UnitOfTime): TimeUnit { + const unit: TimeUnit = {}; + switch (units) { + case 'year': + unit.month = 0; + /* falls through */ + case 'month': + unit.day = 1; + /* falls through */ + case 'week': + case 'day': + unit.hour = 0; + /* falls through */ + case 'hour': + unit.minute = 0; + /* falls through */ + case 'minute': + unit.seconds = 0; + /* falls through */ + // case 'second': + // this.milliseconds(0); + } + + return unit; +} diff --git a/src/datepicker/engine/calc-month-view.ts b/src/datepicker/engine/calc-month-view.ts index f0d4f3769a..982f68bb5f 100644 --- a/src/datepicker/engine/calc-month-view.ts +++ b/src/datepicker/engine/calc-month-view.ts @@ -4,7 +4,7 @@ import { DaysCalendarModel, MonthViewOptions } from '../models/index'; import { getFirstDayOfMonth } from '../../bs-moment/utils/date-getters'; import { getStartingDayOfCalendar } from '../utils/bs-calendar-utils'; -import { changeDate } from '../utils/date-utils'; +import { shiftDate } from '../../bs-moment/utils/date-setters'; export function calculateMonthModel(date: Date, options: MonthViewOptions): DaysCalendarModel { const firstDay = getFirstDayOfMonth(date); @@ -15,7 +15,7 @@ export function calculateMonthModel(date: Date, options: MonthViewOptions): Days daysCalendar[i] = new Array(options.width); for (let j = 0; j < options.width; j++) { daysCalendar[i][j] = prevValue; - prevValue = changeDate(prevValue, {day: 1}); + prevValue = shiftDate(prevValue, {day: 1}); } } diff --git a/src/datepicker/models/index.ts b/src/datepicker/models/index.ts index 436c78033f..60200f7003 100644 --- a/src/datepicker/models/index.ts +++ b/src/datepicker/models/index.ts @@ -1,4 +1,5 @@ import { Locale } from '../../bs-moment/locale/locale.class'; +import { TimeUnit } from '../../bs-moment/types'; export interface DaysCalendarModel { daysMatrix: Date[][]; @@ -58,23 +59,8 @@ export interface DatepickerRenderOptions { displayMonths?: number; } -export interface TimeUnit { - year?: number; - month?: number; - day?: number; - hour?: number; - minute?: number; - seconds?: number; -} - export type DateFormatterFn = (date: Date, format: string, locale?: Locale) => string; -export interface LocaleData { - invalidDate: string; - postformat: (str: string) => string; - ordinal: (str: string) => string; -} - // events export interface BsNavigationEvent { diff --git a/src/datepicker/reducer/bs-datepicker.actions.ts b/src/datepicker/reducer/bs-datepicker.actions.ts index 556f81b29e..327eb2977e 100644 --- a/src/datepicker/reducer/bs-datepicker.actions.ts +++ b/src/datepicker/reducer/bs-datepicker.actions.ts @@ -1,6 +1,7 @@ import { Injectable } from '@angular/core'; -import { DatepickerRenderOptions, DayHoverEvent, TimeUnit } from '../models/index'; +import { DatepickerRenderOptions, DayHoverEvent } from '../models/index'; import { Action } from '../../mini-ngrx/index'; +import { TimeUnit } from '../../bs-moment/types'; @Injectable() export class BsDatepickerActions { diff --git a/src/datepicker/reducer/bs-datepicker.reducer.ts b/src/datepicker/reducer/bs-datepicker.reducer.ts index 4480d9e14b..5ba1cbe80b 100644 --- a/src/datepicker/reducer/bs-datepicker.reducer.ts +++ b/src/datepicker/reducer/bs-datepicker.reducer.ts @@ -3,8 +3,8 @@ import { Action } from '../../mini-ngrx/index'; import { BsDatepickerActions } from './bs-datepicker.actions'; import { calculateMonthModel } from '../engine/calc-month-view'; import { formatMonthView } from '../engine/format-month-view'; -import { changeDate } from '../utils/date-utils'; import { flagMonthView } from '../engine/flag-month-view'; +import { shiftDate } from '../../bs-moment/utils/date-setters'; export function bsDatepickerReducer(state = initialDatepickerState, action: Action): BsDatepickerState { switch (action.type) { @@ -25,7 +25,7 @@ export function bsDatepickerReducer(state = initialDatepickerState, action: Acti for (let monthIndex = 0; monthIndex < displayMonths; monthIndex++) { // todo: for unlinked calendars it will be harder monthsModel[monthIndex] = calculateMonthModel(viewDate, state.monthViewOptions); - viewDate = changeDate(viewDate, {month: 1}); + viewDate = shiftDate(viewDate, {month: 1}); } return Object.assign({}, state, {monthsModel}); } @@ -49,7 +49,7 @@ export function bsDatepickerReducer(state = initialDatepickerState, action: Acti } case(BsDatepickerActions.STEP_NAVIGATION): { - const viewDate = changeDate(state.viewDate, action.payload); + const viewDate = shiftDate(state.viewDate, action.payload); return Object.assign({}, state, {viewDate}); } diff --git a/src/datepicker/themes/bs/bs-datepicker-navigation-view.component.ts b/src/datepicker/themes/bs/bs-datepicker-navigation-view.component.ts index b826ead653..efa5aa3441 100644 --- a/src/datepicker/themes/bs/bs-datepicker-navigation-view.component.ts +++ b/src/datepicker/themes/bs/bs-datepicker-navigation-view.component.ts @@ -1,5 +1,6 @@ import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; -import { BsNavigationEvent, MonthViewModel, TimeUnit } from '../../models/index'; +import { BsNavigationEvent, MonthViewModel } from '../../models/index'; +import { TimeUnit } from '../../../bs-moment/types'; @Component({ selector: 'bs-datepicker-navigation-view', diff --git a/src/datepicker/utils/bs-calendar-utils.ts b/src/datepicker/utils/bs-calendar-utils.ts index 545b1d281c..18dc1bb445 100644 --- a/src/datepicker/utils/bs-calendar-utils.ts +++ b/src/datepicker/utils/bs-calendar-utils.ts @@ -1,5 +1,5 @@ import { getDayOfWeek, isFirstDayOfWeek } from '../../bs-moment/utils/date-getters'; -import { changeDate } from './date-utils'; +import { shiftDate } from '../../bs-moment/utils/date-setters'; export function getStartingDayOfCalendar(date: Date, options: {firstDayOfWeek?: number}): Date { if (isFirstDayOfWeek(date, options.firstDayOfWeek)) { @@ -8,5 +8,5 @@ export function getStartingDayOfCalendar(date: Date, options: {firstDayOfWeek?: const weekDay = getDayOfWeek(date); - return changeDate(date, {day: -weekDay}); + return shiftDate(date, {day: -weekDay}); } diff --git a/src/locale.ts b/src/locale.ts new file mode 100644 index 0000000000..7accda2bec --- /dev/null +++ b/src/locale.ts @@ -0,0 +1 @@ +export { ar } from './bs-moment/i18n/ar'; diff --git a/src/tsconfig.json b/src/tsconfig.json index 8acf8cd342..aec64038d8 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -27,7 +27,8 @@ "files": [ "../scripts/typings.d.ts", "./ng2-bootstrap.ts", - "./index.ts" + "./index.ts", + "./locale.ts" ], "angularCompilerOptions": { "genDir": "../temp/factories", diff --git a/src/tsconfig.spec.json b/src/tsconfig.spec.json index 65cfed7154..66b0b11e55 100644 --- a/src/tsconfig.spec.json +++ b/src/tsconfig.spec.json @@ -29,7 +29,7 @@ }, "include": [ "../scripts/typings.d.ts", - "**/*.spec.ts", + "**/*.ts", "**/*.d.ts" ], "exclude": [