diff --git a/app/index.html b/app/index.html index 7f9b0a7..ba52957 100644 --- a/app/index.html +++ b/app/index.html @@ -39,7 +39,13 @@

Custom formats

{{format}}
- +
+ +

First day

+
+
First Day: {{weekDay[0]}}
+
+

Minimum / Maximum dates

@@ -187,6 +193,16 @@

Select a date from either picker

"lll", ]; + $scope.weekDays = [ + ['Sunday', 0], + ['Monday', 1], + ['Tuesday', 2], + ['Wednesday', 3], + ['Thursday', 4], + ['Friday', 5], + ['Saturday', 6], + ]; + $scope.timezones = [ ['London, UK', 'Europe/London'], ['Hong Kong, China', 'Asia/Hong_Kong'], diff --git a/app/scripts/datePicker.js b/app/scripts/datePicker.js index b73f80c..3f2f1b5 100644 --- a/app/scripts/datePicker.js +++ b/app/scripts/datePicker.js @@ -24,10 +24,15 @@ Module.constant('datePickerConfig', { //Moment format filter. Module.filter('mFormat', function () { return function (m, format, tz) { - if (!(moment.isMoment(m))) { - return moment(m).format(format); + if (!m) { + return ''; + } + + if (tz) { + return moment.tz(m, tz).format(format); + } else { + return moment.isMoment(m) ? m.format(format) : moment(m).format(format); } - return tz ? moment.tz(m, tz).format(format) : m.format(format); }; }); diff --git a/app/scripts/datePickerUtils.js b/app/scripts/datePickerUtils.js index 9925cbd..8168d4b 100644 --- a/app/scripts/datePickerUtils.js +++ b/app/scripts/datePickerUtils.js @@ -156,13 +156,15 @@ angular.module('datePicker').factory('datePickerUtils', function () { scopeSearch: function (scope, name, comparisonFn) { var parentScope = scope, nameArray = name.split('.'), - target, i, j = nameArray.length; + target, i, j = nameArray.length, + lastTarget; do { - target = parentScope = parentScope.$parent; + lastTarget = target = parentScope = parentScope.$parent; //Loop through provided names. for (i = 0; i < j; i++) { + lastTarget = target; target = target[nameArray[i]]; if (!target) { continue; @@ -174,7 +176,7 @@ angular.module('datePicker').factory('datePickerUtils', function () { //function. If the comparison function is happy, return //found result. Otherwise, continue to the next parent scope if (target && comparisonFn(target)) { - return target; + return [target, lastTarget]; } } while (parentScope.$parent); @@ -183,17 +185,24 @@ angular.module('datePicker').factory('datePickerUtils', function () { }, findFunction: function (scope, name) { //Search scope ancestors for a matching function. - return this.scopeSearch(scope, name, function (target) { + var result = this.scopeSearch(scope, name, function (target) { //Property must also be a function return angular.isFunction(target); }); + + + return result ? function () { + result[0].apply(result[1], arguments); + } : false; }, findParam: function (scope, name) { //Search scope ancestors for a matching parameter. - return this.scopeSearch(scope, name, function () { + var result = this.scopeSearch(scope, name, function () { //As long as the property exists, we're good return true; }); + + return result ? result[0] : false; }, createMoment: function (m) { if (tz) { diff --git a/dist/angular-datepicker.js b/dist/angular-datepicker.js index fb1dc57..e2441da 100644 --- a/dist/angular-datepicker.js +++ b/dist/angular-datepicker.js @@ -1,959 +1,955 @@ (function (global, factory) {'use strict';var fnc;fnc = (typeof exports === 'object' && typeof module !== 'undefined') ? module.exports = factory(require('angular'), require('moment')) :(typeof define === 'function' && define.amd) ? define(['angular', 'moment'], factory) :factory(global.angular, global.moment);}(this, function (angular, moment) { -//(function (global, factory) { -// 'use strict'; -// var fnc; -// fnc = (typeof exports === 'object' && typeof module !== 'undefined') ? module.exports = factory(require('angular'), require('moment')) : -// (typeof define === 'function' && define.amd) ? define(['angular', 'moment'], factory) : -// factory(global.angular, global.moment); -//}(this, function (angular, moment) { var Module = angular.module('datePicker', []); - Module.constant('datePickerConfig', { - template: 'templates/datepicker.html', - view: 'month', - views: ['year', 'month', 'date', 'hours', 'minutes'], - momentNames: { - year: 'year', - month: 'month', - date: 'day', - hours: 'hours', - minutes: 'minutes', - }, - viewConfig: { - year: ['years', 'isSameYear'], - month: ['months', 'isSameMonth'], - hours: ['hours', 'isSameHour'], - minutes: ['minutes', 'isSameMinutes'], - }, - step: 5 - }); +Module.constant('datePickerConfig', { + template: 'templates/datepicker.html', + view: 'month', + views: ['year', 'month', 'date', 'hours', 'minutes'], + momentNames: { + year: 'year', + month: 'month', + date: 'day', + hours: 'hours', + minutes: 'minutes', + }, + viewConfig: { + year: ['years', 'isSameYear'], + month: ['months', 'isSameMonth'], + hours: ['hours', 'isSameHour'], + minutes: ['minutes', 'isSameMinutes'], + }, + step: 5 +}); //Moment format filter. - Module.filter('mFormat', function () { - return function (m, format, tz) { - if (!(moment.isMoment(m))) { - return (m) ? moment(m).format(format) : ''; - } - return tz ? moment.tz(m, tz).format(format) : m.format(format); - }; - }); - - Module.directive('datePicker', ['datePickerConfig', 'datePickerUtils', function datePickerDirective(datePickerConfig, datePickerUtils) { - - //noinspection JSUnusedLocalSymbols - return { - // this is a bug ? - require: '?ngModel', - template: '
', - scope: { - model: '=datePicker', - after: '=?', - before: '=?' - }, - link: function (scope, element, attrs, ngModel) { - function prepareViews() { - scope.views = datePickerConfig.views.concat(); - scope.view = attrs.view || datePickerConfig.view; - - scope.views = scope.views.slice( - scope.views.indexOf(attrs.maxView || 'year'), - scope.views.indexOf(attrs.minView || 'minutes') + 1 - ); - - if (scope.views.length === 1 || scope.views.indexOf(scope.view) === -1) { - scope.view = scope.views[0]; - } - } +Module.filter('mFormat', function () { + return function (m, format, tz) { + if (!m) { + return ''; + } - function getDate(name) { - return datePickerUtils.getDate(scope, attrs, name); + if (tz) { + return moment.tz(m, tz).format(format); + } else { + return moment.isMoment(m) ? m.format(format) : moment(m).format(format); + } + }; +}); + +Module.directive('datePicker', ['datePickerConfig', 'datePickerUtils', function datePickerDirective(datePickerConfig, datePickerUtils) { + + //noinspection JSUnusedLocalSymbols + return { + // this is a bug ? + require: '?ngModel', + template: '
', + scope: { + model: '=datePicker', + after: '=?', + before: '=?' + }, + link: function (scope, element, attrs, ngModel) { + function prepareViews() { + scope.views = datePickerConfig.views.concat(); + scope.view = attrs.view || datePickerConfig.view; + + scope.views = scope.views.slice( + scope.views.indexOf(attrs.maxView || 'year'), + scope.views.indexOf(attrs.minView || 'minutes') + 1 + ); + + if (scope.views.length === 1 || scope.views.indexOf(scope.view) === -1) { + scope.view = scope.views[0]; } + } - var arrowClick = false, - tz = scope.tz = attrs.timezone, - createMoment = datePickerUtils.createMoment, - eventIsForPicker = datePickerUtils.eventIsForPicker, - step = parseInt(attrs.step || datePickerConfig.step, 10), - partial = !!attrs.partial, - minDate = getDate('minDate'), - maxDate = getDate('maxDate'), - pickerID = element[0].id, - now = scope.now = createMoment(), - selected = scope.date = createMoment(scope.model || now), - autoclose = attrs.autoClose === 'true', - // Either gets the 1st day from the attributes, or asks moment.js to give it to us as it is localized. - firstDay = attrs.firstDay && attrs.firstDay >= 0 && attrs.firstDay <= 6 ? parseInt(attrs.firstDay, 10) : moment().weekday(0).day(), - setDate, - prepareViewData, - isSame, - clipDate, - isNow, - inValidRange; - - datePickerUtils.setParams(tz, firstDay); + function getDate(name) { + return datePickerUtils.getDate(scope, attrs, name); + } - if (!scope.model) { - selected.minute(Math.ceil(selected.minute() / step) * step).second(0); - } + var arrowClick = false, + tz = scope.tz = attrs.timezone, + createMoment = datePickerUtils.createMoment, + eventIsForPicker = datePickerUtils.eventIsForPicker, + step = parseInt(attrs.step || datePickerConfig.step, 10), + partial = !!attrs.partial, + minDate = getDate('minDate'), + maxDate = getDate('maxDate'), + pickerID = element[0].id, + now = scope.now = createMoment(), + selected = scope.date = createMoment(scope.model || now), + autoclose = attrs.autoClose === 'true', + // Either gets the 1st day from the attributes, or asks moment.js to give it to us as it is localized. + firstDay = attrs.firstDay && attrs.firstDay >= 0 && attrs.firstDay <= 6 ? parseInt(attrs.firstDay, 10) : moment().weekday(0).day(), + setDate, + prepareViewData, + isSame, + clipDate, + isNow, + inValidRange; + + datePickerUtils.setParams(tz, firstDay); + + if (!scope.model) { + selected.minute(Math.ceil(selected.minute() / step) * step).second(0); + } - scope.template = attrs.template || datePickerConfig.template; + scope.template = attrs.template || datePickerConfig.template; - scope.watchDirectChanges = attrs.watchDirectChanges !== undefined; - scope.callbackOnSetDate = attrs.dateChange ? datePickerUtils.findFunction(scope, attrs.dateChange) : undefined; + scope.watchDirectChanges = attrs.watchDirectChanges !== undefined; + scope.callbackOnSetDate = attrs.dateChange ? datePickerUtils.findFunction(scope, attrs.dateChange) : undefined; - prepareViews(); + prepareViews(); - scope.setView = function (nextView) { - if (scope.views.indexOf(nextView) !== -1) { - scope.view = nextView; - } - }; + scope.setView = function (nextView) { + if (scope.views.indexOf(nextView) !== -1) { + scope.view = nextView; + } + }; - scope.selectDate = function (date) { - if (attrs.disabled) { - return false; - } - if (isSame(scope.date, date)) { - date = scope.date; - } - date = clipDate(date); - if (!date) { - return false; - } - scope.date = date; + scope.selectDate = function (date) { + if (attrs.disabled) { + return false; + } + if (isSame(scope.date, date)) { + date = scope.date; + } + date = clipDate(date); + if (!date) { + return false; + } + scope.date = date; - var nextView = scope.views[scope.views.indexOf(scope.view) + 1]; - if ((!nextView || partial) || scope.model) { - setDate(date); - } + var nextView = scope.views[scope.views.indexOf(scope.view) + 1]; + if ((!nextView || partial) || scope.model) { + setDate(date); + } - if (nextView) { - scope.setView(nextView); - } else if (autoclose) { - element.addClass('hidden'); - scope.$emit('hidePicker'); - } else { - prepareViewData(); - } - }; + if (nextView) { + scope.setView(nextView); + } else if (autoclose) { + element.addClass('hidden'); + scope.$emit('hidePicker'); + } else { + prepareViewData(); + } + }; - setDate = function (date) { - if (date) { - scope.model = date; - if (ngModel) { - ngModel.$setViewValue(date); - } + setDate = function (date) { + if (date) { + scope.model = date; + if (ngModel) { + ngModel.$setViewValue(date); } - scope.$emit('setDate', scope.model, scope.view); + } + scope.$emit('setDate', scope.model, scope.view); - //This is duplicated in the new functionality. - if (scope.callbackOnSetDate) { - scope.callbackOnSetDate(attrs.datePicker, scope.date); - } - }; + //This is duplicated in the new functionality. + if (scope.callbackOnSetDate) { + scope.callbackOnSetDate(attrs.datePicker, scope.date); + } + }; - function update() { - var view = scope.view; - datePickerUtils.setParams(tz, firstDay); + function update() { + var view = scope.view; + datePickerUtils.setParams(tz, firstDay); - if (scope.model && !arrowClick) { - scope.date = createMoment(scope.model); - arrowClick = false; - } + if (scope.model && !arrowClick) { + scope.date = createMoment(scope.model); + arrowClick = false; + } - var date = scope.date; - - switch (view) { - case 'year': - scope.years = datePickerUtils.getVisibleYears(date); - break; - case 'month': - scope.months = datePickerUtils.getVisibleMonths(date); - break; - case 'date': - scope.weekdays = scope.weekdays || datePickerUtils.getDaysOfWeek(); - scope.weeks = datePickerUtils.getVisibleWeeks(date); - break; - case 'hours': - scope.hours = datePickerUtils.getVisibleHours(date); - break; - case 'minutes': - scope.minutes = datePickerUtils.getVisibleMinutes(date, step); - break; - } + var date = scope.date; - prepareViewData(); + switch (view) { + case 'year': + scope.years = datePickerUtils.getVisibleYears(date); + break; + case 'month': + scope.months = datePickerUtils.getVisibleMonths(date); + break; + case 'date': + scope.weekdays = scope.weekdays || datePickerUtils.getDaysOfWeek(); + scope.weeks = datePickerUtils.getVisibleWeeks(date); + break; + case 'hours': + scope.hours = datePickerUtils.getVisibleHours(date); + break; + case 'minutes': + scope.minutes = datePickerUtils.getVisibleMinutes(date, step); + break; } - function watch() { - if (scope.view !== 'date') { - return scope.view; - } - return scope.date ? scope.date.month() : null; + prepareViewData(); + } + + function watch() { + if (scope.view !== 'date') { + return scope.view; } + return scope.date ? scope.date.month() : null; + } - scope.$watch(watch, update); + scope.$watch(watch, update); - if (scope.watchDirectChanges) { - scope.$watch('model', function () { - arrowClick = false; - update(); - }); - } + if (scope.watchDirectChanges) { + scope.$watch('model', function () { + arrowClick = false; + update(); + }); + } - prepareViewData = function () { - var view = scope.view, - date = scope.date, - classes = [], classList = '', - i, j; - - datePickerUtils.setParams(tz, firstDay); - - if (view === 'date') { - var weeks = scope.weeks, week; - for (i = 0; i < weeks.length; i++) { - week = weeks[i]; - classes.push([]); - for (j = 0; j < week.length; j++) { - classList = ''; - if (datePickerUtils.isSameDay(date, week[j])) { - classList += 'active'; - } - if (isNow(week[j], view)) { - classList += ' now'; - } - //if (week[j].month() !== date.month()) classList += ' disabled'; - if (week[j].month() !== date.month() || !inValidRange(week[j])) { - classList += ' disabled'; - } - classes[i].push(classList); - } - } - } else { - var params = datePickerConfig.viewConfig[view], - dates = scope[params[0]], - compareFunc = params[1]; + prepareViewData = function () { + var view = scope.view, + date = scope.date, + classes = [], classList = '', + i, j; + + datePickerUtils.setParams(tz, firstDay); - for (i = 0; i < dates.length; i++) { + if (view === 'date') { + var weeks = scope.weeks, week; + for (i = 0; i < weeks.length; i++) { + week = weeks[i]; + classes.push([]); + for (j = 0; j < week.length; j++) { classList = ''; - if (datePickerUtils[compareFunc](date, dates[i])) { + if (datePickerUtils.isSameDay(date, week[j])) { classList += 'active'; } - if (isNow(dates[i], view)) { + if (isNow(week[j], view)) { classList += ' now'; } - if (!inValidRange(dates[i])) { + //if (week[j].month() !== date.month()) classList += ' disabled'; + if (week[j].month() !== date.month() || !inValidRange(week[j])) { classList += ' disabled'; } - classes.push(classList); + classes[i].push(classList); } } - scope.classes = classes; - }; - - scope.next = function (delta) { - var date = moment(scope.date); - delta = delta || 1; - switch (scope.view) { - case 'year': - /*falls through*/ - case 'month': - date.year(date.year() + delta); - break; - case 'date': - date.month(date.month() + delta); - break; - case 'hours': - /*falls through*/ - case 'minutes': - date.hours(date.hours() + delta); - break; - } - date = clipDate(date); - if (date) { - scope.date = date; - arrowClick = true; - update(); - } - }; - - inValidRange = function (date) { - var valid = true; - if (minDate && minDate.isAfter(date)) { - valid = isSame(minDate, date); - } - if (maxDate && maxDate.isBefore(date)) { - valid &= isSame(maxDate, date); - } - return valid; - }; - - isSame = function (date1, date2) { - return date1.isSame(date2, datePickerConfig.momentNames[scope.view]) ? true : false; - }; - - clipDate = function (date) { - if (minDate && minDate.isAfter(date)) { - return minDate; - } else if (maxDate && maxDate.isBefore(date)) { - return maxDate; - } else { - return date; + } else { + var params = datePickerConfig.viewConfig[view], + dates = scope[params[0]], + compareFunc = params[1]; + + for (i = 0; i < dates.length; i++) { + classList = ''; + if (datePickerUtils[compareFunc](date, dates[i])) { + classList += 'active'; + } + if (isNow(dates[i], view)) { + classList += ' now'; + } + if (!inValidRange(dates[i])) { + classList += ' disabled'; + } + classes.push(classList); } - }; + } + scope.classes = classes; + }; + + scope.next = function (delta) { + var date = moment(scope.date); + delta = delta || 1; + switch (scope.view) { + case 'year': + /*falls through*/ + case 'month': + date.year(date.year() + delta); + break; + case 'date': + date.month(date.month() + delta); + break; + case 'hours': + /*falls through*/ + case 'minutes': + date.hours(date.hours() + delta); + break; + } + date = clipDate(date); + if (date) { + scope.date = date; + arrowClick = true; + update(); + } + }; - isNow = function (date, view) { - var is = true; - - switch (view) { - case 'minutes': - is &= ~~(now.minutes() / step) === ~~(date.minutes() / step); - /* falls through */ - case 'hours': - is &= now.hours() === date.hours(); - /* falls through */ - case 'date': - is &= now.date() === date.date(); - /* falls through */ - case 'month': - is &= now.month() === date.month(); - /* falls through */ - case 'year': - is &= now.year() === date.year(); - } - return is; - }; + inValidRange = function (date) { + var valid = true; + if (minDate && minDate.isAfter(date)) { + valid = isSame(minDate, date); + } + if (maxDate && maxDate.isBefore(date)) { + valid &= isSame(maxDate, date); + } + return valid; + }; + + isSame = function (date1, date2) { + return date1.isSame(date2, datePickerConfig.momentNames[scope.view]) ? true : false; + }; + + clipDate = function (date) { + if (minDate && minDate.isAfter(date)) { + return minDate; + } else if (maxDate && maxDate.isBefore(date)) { + return maxDate; + } else { + return date; + } + }; + + isNow = function (date, view) { + var is = true; + + switch (view) { + case 'minutes': + is &= ~~(now.minutes() / step) === ~~(date.minutes() / step); + /* falls through */ + case 'hours': + is &= now.hours() === date.hours(); + /* falls through */ + case 'date': + is &= now.date() === date.date(); + /* falls through */ + case 'month': + is &= now.month() === date.month(); + /* falls through */ + case 'year': + is &= now.year() === date.year(); + } + return is; + }; - scope.prev = function (delta) { - return scope.next(-delta || -1); - }; + scope.prev = function (delta) { + return scope.next(-delta || -1); + }; - if (pickerID) { - scope.$on('pickerUpdate', function (event, pickerIDs, data) { - if (eventIsForPicker(pickerIDs, pickerID)) { - var updateViews = false, updateViewData = false; + if (pickerID) { + scope.$on('pickerUpdate', function (event, pickerIDs, data) { + if (eventIsForPicker(pickerIDs, pickerID)) { + var updateViews = false, updateViewData = false; - if (angular.isDefined(data.minDate)) { - minDate = data.minDate ? data.minDate : false; - updateViewData = true; - } - if (angular.isDefined(data.maxDate)) { - maxDate = data.maxDate ? data.maxDate : false; - updateViewData = true; - } + if (angular.isDefined(data.minDate)) { + minDate = data.minDate ? data.minDate : false; + updateViewData = true; + } + if (angular.isDefined(data.maxDate)) { + maxDate = data.maxDate ? data.maxDate : false; + updateViewData = true; + } - if (angular.isDefined(data.minView)) { - attrs.minView = data.minView; - updateViews = true; - } - if (angular.isDefined(data.maxView)) { - attrs.maxView = data.maxView; - updateViews = true; - } - attrs.view = data.view || attrs.view; + if (angular.isDefined(data.minView)) { + attrs.minView = data.minView; + updateViews = true; + } + if (angular.isDefined(data.maxView)) { + attrs.maxView = data.maxView; + updateViews = true; + } + attrs.view = data.view || attrs.view; - if (updateViews) { - prepareViews(); - } + if (updateViews) { + prepareViews(); + } - if (updateViewData) { - update(); - } + if (updateViewData) { + update(); } - }); - } + } + }); } - }; - }]); -//})); - -//(function (global, factory) { -// 'use strict'; -// var fnc; -// fnc = (typeof exports === 'object' && typeof module !== 'undefined') ? module.exports = factory(require('angular'), require('moment')) : -// (typeof define === 'function' && define.amd) ? define(['angular', 'moment'], factory) : -// factory(global.angular, global.moment); -//}(this, function (angular, moment) { + } + }; +}]); + angular.module('datePicker').factory('datePickerUtils', function () { - var tz, firstDay; - var createNewDate = function (year, month, day, hour, minute) { - var utc = Date.UTC(year | 0, month | 0, day | 0, hour | 0, minute | 0); - return tz ? moment.tz(utc, tz) : moment(utc); - }; - - return { - getVisibleMinutes: function (m, step) { - var year = m.year(), - month = m.month(), - day = m.date(), - hour = m.hours(), pushedDate, - offset = m.utcOffset() / 60, - minutes = [], minute; - - for (minute = 0; minute < 60; minute += step) { - pushedDate = createNewDate(year, month, day, hour - offset, minute); - minutes.push(pushedDate); - } - return minutes; - }, - getVisibleWeeks: function (m) { - m = moment(m); - var startYear = m.year(), - startMonth = m.month(); + var tz, firstDay; + var createNewDate = function (year, month, day, hour, minute) { + var utc = Date.UTC(year | 0, month | 0, day | 0, hour | 0, minute | 0); + return tz ? moment.tz(utc, tz) : moment(utc); + }; + + return { + getVisibleMinutes: function (m, step) { + var year = m.year(), + month = m.month(), + day = m.date(), + hour = m.hours(), pushedDate, + offset = m.utcOffset() / 60, + minutes = [], minute; + + for (minute = 0; minute < 60; minute += step) { + pushedDate = createNewDate(year, month, day, hour - offset, minute); + minutes.push(pushedDate); + } + return minutes; + }, + getVisibleWeeks: function (m) { + m = moment(m); + var startYear = m.year(), + startMonth = m.month(); - //Set date to the first day of the month - m.date(1); + //Set date to the first day of the month + m.date(1); - //Grab day of the week - var day = m.day(); + //Grab day of the week + var day = m.day(); - //Go back the required number of days to arrive at the previous week start - m.date(firstDay - (day + (firstDay >= day ? 6 : -1))); + //Go back the required number of days to arrive at the previous week start + m.date(firstDay - (day + (firstDay >= day ? 6 : -1))); - var weeks = []; + var weeks = []; - while (weeks.length < 6) { - if (m.year() === startYear && m.month() > startMonth) { - break; - } - weeks.push(this.getDaysOfWeek(m)); - m.add(7, 'd'); + while (weeks.length < 6) { + if (m.year() === startYear && m.month() > startMonth) { + break; } - return weeks; - }, - getVisibleYears: function (d) { - var m = moment(d), - year = m.year(); - - m.year(year - (year % 10)); + weeks.push(this.getDaysOfWeek(m)); + m.add(7, 'd'); + } + return weeks; + }, + getVisibleYears: function (d) { + var m = moment(d), year = m.year(); - var offset = m.utcOffset() / 60, - years = [], - pushedDate, - actualOffset; - - for (var i = 0; i < 12; i++) { - pushedDate = createNewDate(year, 0, 1, 0 - offset); - actualOffset = pushedDate.utcOffset() / 60; - if (actualOffset !== offset) { - pushedDate = createNewDate(year, 0, 1, 0 - actualOffset); - offset = actualOffset; - } - years.push(pushedDate); - year++; + m.year(year - (year % 10)); + year = m.year(); + + var offset = m.utcOffset() / 60, + years = [], + pushedDate, + actualOffset; + + for (var i = 0; i < 12; i++) { + pushedDate = createNewDate(year, 0, 1, 0 - offset); + actualOffset = pushedDate.utcOffset() / 60; + if (actualOffset !== offset) { + pushedDate = createNewDate(year, 0, 1, 0 - actualOffset); + offset = actualOffset; } - return years; - }, - getDaysOfWeek: function (m) { - m = m ? m : (tz ? moment.tz(tz).day(firstDay) : moment().day(firstDay)); - - var year = m.year(), - month = m.month(), - day = m.date(), - days = [], - pushedDate, - offset = m.utcOffset() / 60, - actualOffset; - - for (var i = 0; i < 7; i++) { - pushedDate = createNewDate(year, month, day, 0 - offset, 0, false); - actualOffset = pushedDate.utcOffset() / 60; - if (actualOffset !== offset) { - pushedDate = createNewDate(year, month, day, 0 - actualOffset, 0, false); - } - days.push(pushedDate); - day++; + years.push(pushedDate); + year++; + } + return years; + }, + getDaysOfWeek: function (m) { + m = m ? m : (tz ? moment.tz(tz).day(firstDay) : moment().day(firstDay)); + + var year = m.year(), + month = m.month(), + day = m.date(), + days = [], + pushedDate, + offset = m.utcOffset() / 60, + actualOffset; + + for (var i = 0; i < 7; i++) { + pushedDate = createNewDate(year, month, day, 0 - offset, 0, false); + actualOffset = pushedDate.utcOffset() / 60; + if (actualOffset !== offset) { + pushedDate = createNewDate(year, month, day, 0 - actualOffset, 0, false); } - return days; - }, - getVisibleMonths: function (m) { - var year = m.year(), - offset = m.utcOffset() / 60, - months = [], - pushedDate, - actualOffset; - - for (var month = 0; month < 12; month++) { - pushedDate = createNewDate(year, month, 1, 0 - offset, 0, false); - actualOffset = pushedDate.utcOffset() / 60; - if (actualOffset !== offset) { - pushedDate = createNewDate(year, month, 1, 0 - actualOffset, 0, false); - } - months.push(pushedDate); + days.push(pushedDate); + day++; + } + return days; + }, + getVisibleMonths: function (m) { + var year = m.year(), + offset = m.utcOffset() / 60, + months = [], + pushedDate, + actualOffset; + + for (var month = 0; month < 12; month++) { + pushedDate = createNewDate(year, month, 1, 0 - offset, 0, false); + actualOffset = pushedDate.utcOffset() / 60; + if (actualOffset !== offset) { + pushedDate = createNewDate(year, month, 1, 0 - actualOffset, 0, false); } - return months; - }, - getVisibleHours: function (m) { - var year = m.year(), - month = m.month(), - day = m.date(), - hours = [], - hour, pushedDate, actualOffset, - offset = m.utcOffset() / 60; - - for (hour = 0; hour < 24; hour++) { - pushedDate = createNewDate(year, month, day, hour - offset, 0, false); - actualOffset = pushedDate.utcOffset() / 60; - if (actualOffset !== offset) { - pushedDate = createNewDate(year, month, day, hour - actualOffset, 0, false); - } - hours.push(pushedDate); + months.push(pushedDate); + } + return months; + }, + getVisibleHours: function (m) { + var year = m.year(), + month = m.month(), + day = m.date(), + hours = [], + hour, pushedDate, actualOffset, + offset = m.utcOffset() / 60; + + for (hour = 0; hour < 24; hour++) { + pushedDate = createNewDate(year, month, day, hour - offset, 0, false); + actualOffset = pushedDate.utcOffset() / 60; + if (actualOffset !== offset) { + pushedDate = createNewDate(year, month, day, hour - actualOffset, 0, false); } + hours.push(pushedDate); + } - return hours; - }, - isAfter: function (model, date) { - return model && model.unix() >= date.unix(); - }, - isBefore: function (model, date) { - return model.unix() <= date.unix(); - }, - isSameYear: function (model, date) { - return model && model.year() === date.year(); - }, - isSameMonth: function (model, date) { - return this.isSameYear(model, date) && model.month() === date.month(); - }, - isSameDay: function (model, date) { - return this.isSameMonth(model, date) && model.date() === date.date(); - }, - isSameHour: function (model, date) { - return this.isSameDay(model, date) && model.hours() === date.hours(); - }, - isSameMinutes: function (model, date) { - return this.isSameHour(model, date) && model.minutes() === date.minutes(); - }, - setParams: function (zone, fd) { - tz = zone; - firstDay = fd; - }, - scopeSearch: function (scope, name, comparisonFn) { - var parentScope = scope, - nameArray = name.split('.'), - target, i, j = nameArray.length; - - do { - target = parentScope = parentScope.$parent; - - //Loop through provided names. - for (i = 0; i < j; i++) { - target = target[nameArray[i]]; - if (!target) { - continue; - } - } - - //If we reached the end of the list for this scope, - //and something was found, trigger the comparison - //function. If the comparison function is happy, return - //found result. Otherwise, continue to the next parent scope - if (target && comparisonFn(target)) { - return target; + return hours; + }, + isAfter: function (model, date) { + return model && model.unix() >= date.unix(); + }, + isBefore: function (model, date) { + return model.unix() <= date.unix(); + }, + isSameYear: function (model, date) { + return model && model.year() === date.year(); + }, + isSameMonth: function (model, date) { + return this.isSameYear(model, date) && model.month() === date.month(); + }, + isSameDay: function (model, date) { + return this.isSameMonth(model, date) && model.date() === date.date(); + }, + isSameHour: function (model, date) { + return this.isSameDay(model, date) && model.hours() === date.hours(); + }, + isSameMinutes: function (model, date) { + return this.isSameHour(model, date) && model.minutes() === date.minutes(); + }, + setParams: function (zone, fd) { + tz = zone; + firstDay = fd; + }, + scopeSearch: function (scope, name, comparisonFn) { + var parentScope = scope, + nameArray = name.split('.'), + target, i, j = nameArray.length, + lastTarget; + + do { + lastTarget = target = parentScope = parentScope.$parent; + + //Loop through provided names. + for (i = 0; i < j; i++) { + lastTarget = target; + target = target[nameArray[i]]; + if (!target) { + continue; } - - } while (parentScope.$parent); - - return false; - }, - findFunction: function (scope, name) { - //Search scope ancestors for a matching function. - return this.scopeSearch(scope, name, function (target) { - //Property must also be a function - return angular.isFunction(target); - }); - }, - findParam: function (scope, name) { - //Search scope ancestors for a matching parameter. - return this.scopeSearch(scope, name, function () { - //As long as the property exists, we're good - return true; - }); - }, - createMoment: function (m) { - if (tz) { - return moment.tz(m, tz); - } else { - //If input is a moment, and we have no TZ info, we need to remove TZ - //info from the moment, otherwise the newly created moment will take - //the timezone of the input moment. The easiest way to do that is to - //take the unix timestamp, and use that to create a new moment. - //The new moment will use the local timezone of the user machine. - return moment.isMoment(m) ? moment.unix(m.unix()) : moment(m); } - }, - getDate: function (scope, attrs, name) { - var result = false; - if (attrs[name]) { - result = this.createMoment(attrs[name]); - if (!result.isValid()) { - result = this.findParam(scope, attrs[name]); - if (result) { - result = this.createMoment(result); - } - } + + //If we reached the end of the list for this scope, + //and something was found, trigger the comparison + //function. If the comparison function is happy, return + //found result. Otherwise, continue to the next parent scope + if (target && comparisonFn(target)) { + return [target, lastTarget]; } - return result; - }, - eventIsForPicker: function (targetIDs, pickerID) { - //Checks if an event targeted at a specific picker, via either a string name, or an array of strings. - return (angular.isArray(targetIDs) && targetIDs.indexOf(pickerID) > -1 || targetIDs === pickerID); - } - }; - }); -//})); - -//(function (global, factory) { -// 'use strict'; -// var fnc; -// fnc = (typeof exports === 'object' && typeof module !== 'undefined') ? module.exports = factory(require('angular'), require('moment')) : -// (typeof define === 'function' && define.amd) ? define(['angular', 'moment'], factory) : -// factory(global.angular, global.moment); -//}(this, function (angular, moment) { -var Module = angular.module('datePicker'); + } while (parentScope.$parent); - Module.directive('dateRange', ['$compile', 'datePickerUtils', 'dateTimeConfig', function ($compile, datePickerUtils, dateTimeConfig) { - function getTemplate(attrs, id, model, min, max) { - return dateTimeConfig.template(angular.extend(attrs, { - ngModel: model, - minDate: min && moment.isMoment(min) ? min.format() : false, - maxDate: max && moment.isMoment(max) ? max.format() : false - }), id); - } + return false; + }, + findFunction: function (scope, name) { + //Search scope ancestors for a matching function. + var result = this.scopeSearch(scope, name, function (target) { + //Property must also be a function + return angular.isFunction(target); + }); - function randomName() { - return 'picker' + Math.random().toString().substr(2); - } - return { - scope: { - start: '=', - end: '=' - }, - link: function (scope, element, attrs) { - var dateChange = null, - pickerRangeID = element[0].id, - pickerIDs = [randomName(), randomName()], - createMoment = datePickerUtils.createMoment, - eventIsForPicker = datePickerUtils.eventIsForPicker; - - scope.dateChange = function (modelName, newDate) { - //Notify user if callback exists. - if (dateChange) { - dateChange(modelName, newDate); + return result ? function () { + result[0].apply(result[1], arguments); + } : false; + }, + findParam: function (scope, name) { + //Search scope ancestors for a matching parameter. + var result = this.scopeSearch(scope, name, function () { + //As long as the property exists, we're good + return true; + }); + + return result ? result[0] : false; + }, + createMoment: function (m) { + if (tz) { + return moment.tz(m, tz); + } else { + //If input is a moment, and we have no TZ info, we need to remove TZ + //info from the moment, otherwise the newly created moment will take + //the timezone of the input moment. The easiest way to do that is to + //take the unix timestamp, and use that to create a new moment. + //The new moment will use the local timezone of the user machine. + return moment.isMoment(m) ? moment.unix(m.unix()) : moment(m); + } + }, + getDate: function (scope, attrs, name) { + var result = false; + if (attrs[name]) { + result = this.createMoment(attrs[name]); + if (!result.isValid()) { + result = this.findParam(scope, attrs[name]); + if (result) { + result = this.createMoment(result); } - }; - - function setMax(date) { - scope.$broadcast('pickerUpdate', pickerIDs[0], { - maxDate: date - }); } + } - function setMin(date) { - scope.$broadcast('pickerUpdate', pickerIDs[1], { - minDate: date - }); + return result; + }, + //Checks if an event targeted at a specific picker, via either a string name, or an array of strings. + eventIsForPicker: function (targetIDs, pickerID) { + function matches(id) { + if (id instanceof RegExp) { + return id.test(pickerID); } + return id === pickerID; + } - if (pickerRangeID) { - scope.$on('pickerUpdate', function (event, targetIDs, data) { - if (eventIsForPicker(targetIDs, pickerRangeID)) { - //If we received an update event, dispatch it to the inner pickers using their IDs. - scope.$broadcast('pickerUpdate', pickerIDs, data); - } - }); + if (angular.isArray(targetIDs)) { + return targetIDs.some(matches); + } + return matches(targetIDs); + } + }; +}); + +var Module = angular.module('datePicker'); + +Module.directive('dateRange', ['$compile', 'datePickerUtils', 'dateTimeConfig', function ($compile, datePickerUtils, dateTimeConfig) { + function getTemplate(attrs, id, model, min, max) { + return dateTimeConfig.template(angular.extend(attrs, { + ngModel: model, + minDate: min && moment.isMoment(min) ? min.format() : false, + maxDate: max && moment.isMoment(max) ? max.format() : false + }), id); + } + + function randomName() { + return 'picker' + Math.random().toString().substr(2); + } + + return { + scope: { + start: '=', + end: '=' + }, + link: function (scope, element, attrs) { + var dateChange = null, + pickerRangeID = element[0].id, + pickerIDs = [randomName(), randomName()], + createMoment = datePickerUtils.createMoment, + eventIsForPicker = datePickerUtils.eventIsForPicker; + + scope.dateChange = function (modelName, newDate) { + //Notify user if callback exists. + if (dateChange) { + dateChange(modelName, newDate); } + }; - datePickerUtils.setParams(attrs.timezone); + function setMax(date) { + scope.$broadcast('pickerUpdate', pickerIDs[0], { + maxDate: date + }); + } - scope.start = createMoment(scope.start); - scope.end = createMoment(scope.end); + function setMin(date) { + scope.$broadcast('pickerUpdate', pickerIDs[1], { + minDate: date + }); + } - scope.$watchGroup(['start', 'end'], function (dates) { - //Scope data changed, update picker min/max - setMin(dates[0]); - setMax(dates[1]); + if (pickerRangeID) { + scope.$on('pickerUpdate', function (event, targetIDs, data) { + if (eventIsForPicker(targetIDs, pickerRangeID)) { + //If we received an update event, dispatch it to the inner pickers using their IDs. + scope.$broadcast('pickerUpdate', pickerIDs, data); + } }); + } - if (angular.isDefined(attrs.dateChange)) { - dateChange = datePickerUtils.findFunction(scope, attrs.dateChange); - } + datePickerUtils.setParams(attrs.timezone); - attrs.onSetDate = 'dateChange'; + scope.start = createMoment(scope.start); + scope.end = createMoment(scope.end); - var template = '
' + - getTemplate(attrs, pickerIDs[0], 'start', false, scope.end) + - '' + - getTemplate(attrs, pickerIDs[1], 'end', scope.start, false) + - '
'; + scope.$watchGroup(['start', 'end'], function (dates) { + //Scope data changed, update picker min/max + setMin(dates[0]); + setMax(dates[1]); + }); - var picker = $compile(template)(scope); - element.append(picker); + if (angular.isDefined(attrs.dateChange)) { + dateChange = datePickerUtils.findFunction(scope, attrs.dateChange); } - }; - }]); -//})); - -//(function (global, factory) { -// 'use strict'; -// var fnc; -// fnc = (typeof exports === 'object' && typeof module !== 'undefined') ? module.exports = factory(require('angular'), require('moment')) : -// (typeof define === 'function' && define.amd) ? define(['angular', 'moment'], factory) : -// factory(global.angular, global.moment); -//}(this, function (angular, moment) { + + attrs.onSetDate = 'dateChange'; + + var template = '
' + + getTemplate(attrs, pickerIDs[0], 'start', false, scope.end) + + '' + + getTemplate(attrs, pickerIDs[1], 'end', scope.start, false) + + '
'; + + var picker = $compile(template)(scope); + element.append(picker); + } + }; +}]); + var PRISTINE_CLASS = 'ng-pristine', - DIRTY_CLASS = 'ng-dirty'; - - var Module = angular.module('datePicker'); - - Module.constant('dateTimeConfig', { - template: function (attrs, id) { - return '' + - '
'; - }, - format: 'YYYY-MM-DD HH:mm', - views: ['date', 'year', 'month', 'hours', 'minutes'], - autoClose: false, - position: 'relative' - }); - - Module.directive('dateTimeAppend', function () { - return { - link: function (scope, element) { - element.bind('click', function () { - element.find('input')[0].focus(); - }); + DIRTY_CLASS = 'ng-dirty'; + +var Module = angular.module('datePicker'); + +Module.constant('dateTimeConfig', { + template: function (attrs, id) { + return '' + + '
'; + }, + format: 'YYYY-MM-DD HH:mm', + views: ['date', 'year', 'month', 'hours', 'minutes'], + autoClose: false, + position: 'relative' +}); + +Module.directive('dateTimeAppend', function () { + return { + link: function (scope, element) { + element.bind('click', function () { + element.find('input')[0].focus(); + }); + } + }; +}); + +Module.directive('dateTime', ['$compile', '$document', '$filter', 'dateTimeConfig', '$parse', 'datePickerUtils', function ($compile, $document, $filter, dateTimeConfig, $parse, datePickerUtils) { + var body = $document.find('body'); + var dateFilter = $filter('mFormat'); + + return { + require: 'ngModel', + scope: true, + link: function (scope, element, attrs, ngModel) { + var format = attrs.format || dateTimeConfig.format, + parentForm = element.inheritedData('$formController'), + views = $parse(attrs.views)(scope) || dateTimeConfig.views.concat(), + view = attrs.view || views[0], + index = views.indexOf(view), + dismiss = attrs.autoClose ? $parse(attrs.autoClose)(scope) : dateTimeConfig.autoClose, + picker = null, + pickerID = element[0].id, + position = attrs.position || dateTimeConfig.position, + container = null, + minDate = null, + minValid = null, + maxDate = null, + maxValid = null, + timezone = attrs.timezone || false, + eventIsForPicker = datePickerUtils.eventIsForPicker, + dateChange = null, + shownOnce = false, + template; + + if (index === -1) { + views.splice(index, 1); } - }; - }); - - Module.directive('dateTime', ['$compile', '$document', '$filter', 'dateTimeConfig', '$parse', 'datePickerUtils', function ($compile, $document, $filter, dateTimeConfig, $parse, datePickerUtils) { - var body = $document.find('body'); - var dateFilter = $filter('mFormat'); - - return { - require: 'ngModel', - scope: true, - link: function (scope, element, attrs, ngModel) { - var format = attrs.format || dateTimeConfig.format, - parentForm = element.inheritedData('$formController'), - views = $parse(attrs.views)(scope) || dateTimeConfig.views.concat(), - view = attrs.view || views[0], - index = views.indexOf(view), - dismiss = attrs.autoClose ? $parse(attrs.autoClose)(scope) : dateTimeConfig.autoClose, - picker = null, - pickerID = element[0].id, - position = attrs.position || dateTimeConfig.position, - container = null, - minDate = null, - minValid = null, - maxDate = null, - maxValid = null, - timezone = attrs.timezone || false, - eventIsForPicker = datePickerUtils.eventIsForPicker, - dateChange = null, - shownOnce = false, - template; - - if (index === -1) { - views.splice(index, 1); - } - views.unshift(view); + views.unshift(view); - function formatter(value) { + function formatter(value) { + if (value) { return dateFilter(value, format, timezone); } + } - function parser(viewValue) { - if (viewValue.length === format.length) { - return viewValue; - } - return (viewValue.length === 0) ? viewValue : undefined; + function parser(viewValue) { + if (!viewValue) { + return ''; } - - function setMin(date) { - minDate = date; - attrs.minDate = date ? date.format() : date; - minValid = moment.isMoment(date); + var parsed = moment(viewValue, format); + if (parsed.isValid()) { + return parsed; } + } - function setMax(date) { - maxDate = date; - attrs.maxDate = date ? date.format() : date; - maxValid = moment.isMoment(date); - } + function setMin(date) { + minDate = date; + attrs.minDate = date ? date.format() : date; + minValid = moment.isMoment(date); + } - ngModel.$formatters.push(formatter); - ngModel.$parsers.unshift(parser); + function setMax(date) { + maxDate = date; + attrs.maxDate = date ? date.format() : date; + maxValid = moment.isMoment(date); + } - if (angular.isDefined(attrs.minDate)) { - setMin(datePickerUtils.findParam(scope, attrs.minDate)); + ngModel.$formatters.push(formatter); + ngModel.$parsers.unshift(parser); - ngModel.$validators.min = function (value) { - //If we don't have a min / max value, then any value is valid. - return minValid ? moment.isMoment(value) && (minDate.isSame(value) || minDate.isBefore(value)) : true; - }; - } + if (angular.isDefined(attrs.minDate)) { + setMin(datePickerUtils.findParam(scope, attrs.minDate)); + + ngModel.$validators.min = function (value) { + //If we don't have a min / max value, then any value is valid. + return minValid ? moment.isMoment(value) && (minDate.isSame(value) || minDate.isBefore(value)) : true; + }; + } - if (angular.isDefined(attrs.maxDate)) { - setMax(datePickerUtils.findParam(scope, attrs.maxDate)); + if (angular.isDefined(attrs.maxDate)) { + setMax(datePickerUtils.findParam(scope, attrs.maxDate)); - ngModel.$validators.max = function (value) { - return maxValid ? moment.isMoment(value) && (maxDate.isSame(value) || maxDate.isAfter(value)) : true; - }; - } + ngModel.$validators.max = function (value) { + return maxValid ? moment.isMoment(value) && (maxDate.isSame(value) || maxDate.isAfter(value)) : true; + }; + } - if (angular.isDefined(attrs.dateChange)) { - dateChange = datePickerUtils.findFunction(scope, attrs.dateChange); - } + if (angular.isDefined(attrs.dateChange)) { + dateChange = datePickerUtils.findFunction(scope, attrs.dateChange); + } - function getTemplate() { - template = dateTimeConfig.template(attrs); - } + function getTemplate() { + template = dateTimeConfig.template(attrs); + } - function updateInput(event) { - event.stopPropagation(); - if (ngModel.$pristine) { - ngModel.$dirty = true; - ngModel.$pristine = false; - element.removeClass(PRISTINE_CLASS).addClass(DIRTY_CLASS); - if (parentForm) { - parentForm.$setDirty(); - } - ngModel.$render(); + function updateInput(event) { + event.stopPropagation(); + if (ngModel.$pristine) { + ngModel.$dirty = true; + ngModel.$pristine = false; + element.removeClass(PRISTINE_CLASS).addClass(DIRTY_CLASS); + if (parentForm) { + parentForm.$setDirty(); } + ngModel.$render(); } + } - function clear() { - if (picker) { - picker.remove(); - picker = null; - } - if (container) { - container.remove(); - container = null; - } + function clear() { + if (picker) { + picker.remove(); + picker = null; } - - if (pickerID) { - scope.$on('pickerUpdate', function (event, pickerIDs, data) { - if (eventIsForPicker(pickerIDs, pickerID)) { - if (picker) { - //Need to handle situation where the data changed but the picker is currently open. - //To handle this, we can create the inner picker with a random ID, then forward - //any events received to it. - } else { - var validateRequired = false; - if (angular.isDefined(data.minDate)) { - setMin(data.minDate); - validateRequired = true; - } - if (angular.isDefined(data.maxDate)) { - setMax(data.maxDate); - validateRequired = true; - } - - if (angular.isDefined(data.minView)) { - attrs.minView = data.minView; - } - if (angular.isDefined(data.maxView)) { - attrs.maxView = data.maxView; - } - attrs.view = data.view || attrs.view; - - if (validateRequired) { - ngModel.$validate(); - } - if (angular.isDefined(data.format)) { - format = attrs.format = data.format || dateTimeConfig.format; - ngModel.$modelValue = -1; //Triggers formatters. This value will be discarded. - } - getTemplate(); - } - } - }); + if (container) { + container.remove(); + container = null; } + } - function showPicker() { - if (picker) { - return; - } - // create picker element - picker = $compile(template)(scope); - scope.$digest(); - - //If the picker has already been shown before then we shouldn't be binding to events, as these events are already bound to in this scope. - if (!shownOnce) { - scope.$on('setDate', function (event, date, view) { - updateInput(event); - if (dateChange) { - dateChange(attrs.ngModel, date); + if (pickerID) { + scope.$on('pickerUpdate', function (event, pickerIDs, data) { + if (eventIsForPicker(pickerIDs, pickerID)) { + if (picker) { + //Need to handle situation where the data changed but the picker is currently open. + //To handle this, we can create the inner picker with a random ID, then forward + //any events received to it. + } else { + var validateRequired = false; + if (angular.isDefined(data.minDate)) { + setMin(data.minDate); + validateRequired = true; } - if (dismiss && views[views.length - 1] === view) { - clear(); + if (angular.isDefined(data.maxDate)) { + setMax(data.maxDate); + validateRequired = true; } - }); - scope.$on('hidePicker', function () { - element[0].blur(); - }); - - scope.$on('$destroy', clear); + if (angular.isDefined(data.minView)) { + attrs.minView = data.minView; + } + if (angular.isDefined(data.maxView)) { + attrs.maxView = data.maxView; + } + attrs.view = data.view || attrs.view; - shownOnce = true; + if (validateRequired) { + ngModel.$validate(); + } + if (angular.isDefined(data.format)) { + format = attrs.format = data.format || dateTimeConfig.format; + ngModel.$modelValue = -1; //Triggers formatters. This value will be discarded. + } + getTemplate(); + } } + }); + } + function showPicker() { + if (picker) { + return; + } + // create picker element + picker = $compile(template)(scope); + scope.$digest(); + + //If the picker has already been shown before then we shouldn't be binding to events, as these events are already bound to in this scope. + if (!shownOnce) { + scope.$on('setDate', function (event, date, view) { + updateInput(event); + if (dateChange) { + dateChange(attrs.ngModel, date); + } + if (dismiss && views[views.length - 1] === view) { + clear(); + } + }); - // move picker below input element - - if (position === 'absolute') { - var pos = element[0].getBoundingClientRect(); - // Support IE8 - var height = pos.height || element[0].offsetHeight; - picker.css({top: (pos.top + height) + 'px', left: pos.left + 'px', display: 'block', position: position}); - body.append(picker); - } else { - // relative - container = angular.element('
'); - element[0].parentElement.insertBefore(container[0], element[0]); - container.append(picker); - // this approach doesn't work - // element.before(picker); - picker.css({top: element[0].offsetHeight + 'px', display: 'block'}); - } - picker.bind('mousedown', function (evt) { - evt.preventDefault(); + scope.$on('hidePicker', function () { + element[0].blur(); }); + + scope.$on('$destroy', clear); + + shownOnce = true; } - element.bind('focus', showPicker); - element.bind('click', showPicker); - element.bind('blur', clear); - getTemplate(); + + // move picker below input element + + if (position === 'absolute') { + var pos = element[0].getBoundingClientRect(); + // Support IE8 + var height = pos.height || element[0].offsetHeight; + picker.css({top: (pos.top + height) + 'px', left: pos.left + 'px', display: 'block', position: position}); + body.append(picker); + } else { + // relative + container = angular.element('
'); + element[0].parentElement.insertBefore(container[0], element[0]); + container.append(picker); + // this approach doesn't work + // element.before(picker); + picker.css({top: element[0].offsetHeight + 'px', display: 'block'}); + } + picker.bind('mousedown', function (evt) { + evt.preventDefault(); + }); } - }; - }]); -//})); + + element.bind('focus', showPicker); + element.bind('blur', clear); + getTemplate(); + } + }; +}]); angular.module('datePicker').run(['$templateCache', function($templateCache) { $templateCache.put('templates/datepicker.html', @@ -1173,4 +1169,4 @@ $templateCache.put('templates/datepicker.html', ); }]); -})); +})); \ No newline at end of file diff --git a/dist/angular-datepicker.min.js b/dist/angular-datepicker.min.js index d2754ce..86ca258 100644 --- a/dist/angular-datepicker.min.js +++ b/dist/angular-datepicker.min.js @@ -1 +1 @@ -!function(a,b){"use strict";var c;c="object"==typeof exports&&"undefined"!=typeof module?module.exports=b(require("angular"),require("moment")):"function"==typeof define&&define.amd?define(["angular","moment"],b):b(a.angular,a.moment)}(this,function(a,b){var c=a.module("datePicker",[]);c.constant("datePickerConfig",{template:"templates/datepicker.html",view:"month",views:["year","month","date","hours","minutes"],momentNames:{year:"year",month:"month",date:"day",hours:"hours",minutes:"minutes"},viewConfig:{year:["years","isSameYear"],month:["months","isSameMonth"],hours:["hours","isSameHour"],minutes:["minutes","isSameMinutes"]},step:5}),c.filter("mFormat",function(){return function(a,c,d){return b.isMoment(a)?d?b.tz(a,d).format(c):a.format(c):b(a).format(c)}}),c.directive("datePicker",["datePickerConfig","datePickerUtils",function(c,d){return{require:"?ngModel",template:'
',scope:{model:"=datePicker",after:"=?",before:"=?"},link:function(e,f,g,h){function i(){e.views=c.views.concat(),e.view=g.view||c.view,e.views=e.views.slice(e.views.indexOf(g.maxView||"year"),e.views.indexOf(g.minView||"minutes")+1),(1===e.views.length||-1===e.views.indexOf(e.view))&&(e.view=e.views[0])}function j(a){return d.getDate(e,g,a)}function k(){var a=e.view;d.setParams(t,E),e.model&&!s&&(e.date=u(e.model),s=!1);var b=e.date;switch(a){case"year":e.years=d.getVisibleYears(b);break;case"month":e.months=d.getVisibleMonths(b);break;case"date":e.weekdays=e.weekdays||d.getDaysOfWeek(),e.weeks=d.getVisibleWeeks(b);break;case"hours":e.hours=d.getVisibleHours(b);break;case"minutes":e.minutes=d.getVisibleMinutes(b,w)}n()}function l(){return"date"!==e.view?e.view:e.date?e.date.month():null}var m,n,o,p,q,r,s=!1,t=e.tz=g.timezone,u=d.createMoment,v=d.eventIsForPicker,w=parseInt(g.step||c.step,10),x=!!g.partial,y=j("minDate"),z=j("maxDate"),A=f[0].id,B=e.now=u(),C=e.date=u(e.model||B),D="true"===g.autoClose,E=g.firstDay&&g.firstDay>=0&&g.firstDay<=6?parseInt(g.firstDay,10):b().weekday(0).day();d.setParams(t,E),e.model||C.minute(Math.ceil(C.minute()/w)*w).second(0),e.template=g.template||c.template,e.watchDirectChanges=void 0!==g.watchDirectChanges,e.callbackOnSetDate=g.dateChange?d.findFunction(e,g.dateChange):void 0,i(),e.setView=function(a){-1!==e.views.indexOf(a)&&(e.view=a)},e.selectDate=function(a){if(g.disabled)return!1;if(o(e.date,a)&&(a=e.date),a=p(a),!a)return!1;e.date=a;var b=e.views[e.views.indexOf(e.view)+1];(!b||x||e.model)&&m(a),b?e.setView(b):D?(f.addClass("hidden"),e.$emit("hidePicker")):n()},m=function(a){a&&(e.model=a,h&&h.$setViewValue(a)),e.$emit("setDate",e.model,e.view),e.callbackOnSetDate&&e.callbackOnSetDate(g.datePicker,e.date)},e.$watch(l,k),e.watchDirectChanges&&e.$watch("model",function(){s=!1,k()}),n=function(){var a,b,f=e.view,g=e.date,h=[],i="";if(d.setParams(t,E),"date"===f){var j,k=e.weeks;for(a=0;ad;d+=b)c=e(f,g,h,i-j,d),k.push(c);return k},getVisibleWeeks:function(a){a=b(a);var c=a.year(),e=a.month();a.date(1);var f=a.day();a.date(d-(f+(d>=f?6:-1)));for(var g=[];g.length<6&&!(a.year()===c&&a.month()>e);)g.push(this.getDaysOfWeek(a)),a.add(7,"d");return g},getVisibleYears:function(a){var c=b(a),d=c.year();c.year(d-d%10),d=c.year();for(var f,g,h=c.utcOffset()/60,i=[],j=0;12>j;j++)f=e(d,0,1,0-h),g=f.utcOffset()/60,g!==h&&(f=e(d,0,1,0-g),h=g),i.push(f),d++;return i},getDaysOfWeek:function(a){a=a?a:c?b.tz(c).day(d):b().day(d);for(var f,g,h=a.year(),i=a.month(),j=a.date(),k=[],l=a.utcOffset()/60,m=0;7>m;m++)f=e(h,i,j,0-l,0,!1),g=f.utcOffset()/60,g!==l&&(f=e(h,i,j,0-g,0,!1)),k.push(f),j++;return k},getVisibleMonths:function(a){for(var b,c,d=a.year(),f=a.utcOffset()/60,g=[],h=0;12>h;h++)b=e(d,h,1,0-f,0,!1),c=b.utcOffset()/60,c!==f&&(b=e(d,h,1,0-c,0,!1)),g.push(b);return g},getVisibleHours:function(a){var b,c,d,f=a.year(),g=a.month(),h=a.date(),i=[],j=a.utcOffset()/60;for(b=0;24>b;b++)c=e(f,g,h,b-j,0,!1),d=c.utcOffset()/60,d!==j&&(c=e(f,g,h,b-d,0,!1)),i.push(c);return i},isAfter:function(a,b){return a&&a.unix()>=b.unix()},isBefore:function(a,b){return a.unix()<=b.unix()},isSameYear:function(a,b){return a&&a.year()===b.year()},isSameMonth:function(a,b){return this.isSameYear(a,b)&&a.month()===b.month()},isSameDay:function(a,b){return this.isSameMonth(a,b)&&a.date()===b.date()},isSameHour:function(a,b){return this.isSameDay(a,b)&&a.hours()===b.hours()},isSameMinutes:function(a,b){return this.isSameHour(a,b)&&a.minutes()===b.minutes()},setParams:function(a,b){c=a,d=b},scopeSearch:function(a,b,c){var d,e,f=a,g=b.split("."),h=g.length;do{for(d=f=f.$parent,e=0;h>e;e++){d=d[g[e]]}if(d&&c(d))return d}while(f.$parent);return!1},findFunction:function(b,c){return this.scopeSearch(b,c,function(b){return a.isFunction(b)})},findParam:function(a,b){return this.scopeSearch(a,b,function(){return!0})},createMoment:function(a){return c?b.tz(a,c):b.isMoment(a)?b.unix(a.unix()):b(a)},getDate:function(a,b,c){var d=!1;return b[c]&&(d=this.createMoment(b[c]),d.isValid()||(d=this.findParam(a,b[c]),d&&(d=this.createMoment(d)))),d},eventIsForPicker:function(b,c){return a.isArray(b)&&b.indexOf(c)>-1||b===c}}});var c=a.module("datePicker");c.directive("dateRange",["$compile","datePickerUtils","dateTimeConfig",function(c,d,e){function f(c,d,f,g,h){return e.template(a.extend(c,{ngModel:f,minDate:g&&b.isMoment(g)?g.format():!1,maxDate:h&&b.isMoment(h)?h.format():!1}),d)}function g(){return"picker"+Math.random().toString().substr(2)}return{scope:{start:"=",end:"="},link:function(b,e,h){function i(a){b.$broadcast("pickerUpdate",m[0],{maxDate:a})}function j(a){b.$broadcast("pickerUpdate",m[1],{minDate:a})}var k=null,l=e[0].id,m=[g(),g()],n=d.createMoment,o=d.eventIsForPicker;b.dateChange=function(a,b){k&&k(a,b)},l&&b.$on("pickerUpdate",function(a,c,d){o(c,l)&&b.$broadcast("pickerUpdate",m,d)}),d.setParams(h.timezone),b.start=n(b.start),b.end=n(b.end),b.$watchGroup(["start","end"],function(a){j(a[0]),i(a[1])}),a.isDefined(h.dateChange)&&(k=d.findFunction(b,h.dateChange)),h.onSetDate="dateChange";var p='
'+f(h,m[0],"start",!1,b.end)+''+f(h,m[1],"end",b.start,!1)+"
",q=c(p)(b);e.append(q)}}}]);var d="ng-pristine",e="ng-dirty",c=a.module("datePicker");c.constant("dateTimeConfig",{template:function(a,b){return"
'},format:"YYYY-MM-DD HH:mm",views:["date","year","month","hours","minutes"],autoClose:!1,position:"relative"}),c.directive("dateTimeAppend",function(){return{link:function(a,b){b.bind("click",function(){b.find("input")[0].focus()})}}}),c.directive("dateTime",["$compile","$document","$filter","dateTimeConfig","$parse","datePickerUtils",function(c,f,g,h,i,j){var k=f.find("body"),l=g("mFormat");return{require:"ngModel",scope:!0,link:function(f,g,m,n){function o(a){return l(a,x,L)}function p(a){return a.length===x.length?a:void 0}function q(a){H=a,m.minDate=a?a.format():a,I=b.isMoment(a)}function r(a){J=a,m.maxDate=a?a.format():a,K=b.isMoment(a)}function s(){w=h.template(m)}function t(a){a.stopPropagation(),n.$pristine&&(n.$dirty=!0,n.$pristine=!1,g.removeClass(d).addClass(e),y&&y.$setDirty(),n.$render())}function u(){D&&(D.remove(),D=null),G&&(G.remove(),G=null)}function v(){if(!D){if(D=c(w)(f),f.$digest(),O||(f.$on("setDate",function(a,b,c){t(a),N&&N(m.ngModel,b),C&&z[z.length-1]===c&&u()}),f.$on("hidePicker",function(){g.triggerHandler("blur")}),f.$on("$destroy",u),O=!0),"absolute"===F){var b=g[0].getBoundingClientRect(),d=b.height||g[0].offsetHeight;D.css({top:b.top+d+"px",left:b.left+"px",display:"block",position:F}),k.append(D)}else G=a.element("
"),g[0].parentElement.insertBefore(G[0],g[0]),G.append(D),D.css({top:g[0].offsetHeight+"px",display:"block"});D.bind("mousedown",function(a){a.preventDefault()})}}var w,x=m.format||h.format,y=g.inheritedData("$formController"),z=i(m.views)(f)||h.views.concat(),A=m.view||z[0],B=z.indexOf(A),C=m.autoClose?i(m.autoClose)(f):h.autoClose,D=null,E=g[0].id,F=m.position||h.position,G=null,H=null,I=null,J=null,K=null,L=m.timezone||!1,M=j.eventIsForPicker,N=null,O=!1;-1===B&&z.splice(B,1),z.unshift(A),n.$formatters.push(o),n.$parsers.unshift(p),a.isDefined(m.minDate)&&(q(j.findParam(f,m.minDate)),n.$validators.min=function(a){return I?b.isMoment(a)&&(H.isSame(a)||H.isBefore(a)):!0}),a.isDefined(m.maxDate)&&(r(j.findParam(f,m.maxDate)),n.$validators.max=function(a){return K?b.isMoment(a)&&(J.isSame(a)||J.isAfter(a)):!0}),a.isDefined(m.dateChange)&&(N=j.findFunction(f,m.dateChange)),E&&f.$on("pickerUpdate",function(b,c,d){if(M(c,E))if(D);else{var e=!1;a.isDefined(d.minDate)&&(q(d.minDate),e=!0),a.isDefined(d.maxDate)&&(r(d.maxDate),e=!0),a.isDefined(d.minView)&&(m.minView=d.minView),a.isDefined(d.maxView)&&(m.maxView=d.maxView),m.view=d.view||m.view,e&&n.$validate(),a.isDefined(d.format)&&(x=m.format=d.format||h.format,n.$modelValue=-1),s()}}),g.bind("focus",v),g.bind("blur",u),s()}}}]),a.module("datePicker").run(["$templateCache",function(a){a.put("templates/datepicker.html",'
\r\n
\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n \r\n
\r\n
\r\n
\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n \r\n
\r\n
\r\n
\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n \r\n
\r\n
\r\n
\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n \r\n
\r\n
\r\n
\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n \r\n
\r\n
\r\n
')}])}); \ No newline at end of file +!function(a,b){"use strict";var c;c="object"==typeof exports&&"undefined"!=typeof module?module.exports=b(require("angular"),require("moment")):"function"==typeof define&&define.amd?define(["angular","moment"],b):b(a.angular,a.moment)}(this,function(a,b){var c=a.module("datePicker",[]);c.constant("datePickerConfig",{template:"templates/datepicker.html",view:"month",views:["year","month","date","hours","minutes"],momentNames:{year:"year",month:"month",date:"day",hours:"hours",minutes:"minutes"},viewConfig:{year:["years","isSameYear"],month:["months","isSameMonth"],hours:["hours","isSameHour"],minutes:["minutes","isSameMinutes"]},step:5}),c.filter("mFormat",function(){return function(a,c,d){return a?d?b.tz(a,d).format(c):b.isMoment(a)?a.format(c):b(a).format(c):""}}),c.directive("datePicker",["datePickerConfig","datePickerUtils",function(c,d){return{require:"?ngModel",template:'
',scope:{model:"=datePicker",after:"=?",before:"=?"},link:function(e,f,g,h){function i(){e.views=c.views.concat(),e.view=g.view||c.view,e.views=e.views.slice(e.views.indexOf(g.maxView||"year"),e.views.indexOf(g.minView||"minutes")+1),1!==e.views.length&&e.views.indexOf(e.view)!==-1||(e.view=e.views[0])}function j(a){return d.getDate(e,g,a)}function k(){var a=e.view;d.setParams(t,E),e.model&&!s&&(e.date=u(e.model),s=!1);var b=e.date;switch(a){case"year":e.years=d.getVisibleYears(b);break;case"month":e.months=d.getVisibleMonths(b);break;case"date":e.weekdays=e.weekdays||d.getDaysOfWeek(),e.weeks=d.getVisibleWeeks(b);break;case"hours":e.hours=d.getVisibleHours(b);break;case"minutes":e.minutes=d.getVisibleMinutes(b,w)}n()}function l(){return"date"!==e.view?e.view:e.date?e.date.month():null}var m,n,o,p,q,r,s=!1,t=e.tz=g.timezone,u=d.createMoment,v=d.eventIsForPicker,w=parseInt(g.step||c.step,10),x=!!g.partial,y=j("minDate"),z=j("maxDate"),A=f[0].id,B=e.now=u(),C=e.date=u(e.model||B),D="true"===g.autoClose,E=g.firstDay&&g.firstDay>=0&&g.firstDay<=6?parseInt(g.firstDay,10):b().weekday(0).day();d.setParams(t,E),e.model||C.minute(Math.ceil(C.minute()/w)*w).second(0),e.template=g.template||c.template,e.watchDirectChanges=void 0!==g.watchDirectChanges,e.callbackOnSetDate=g.dateChange?d.findFunction(e,g.dateChange):void 0,i(),e.setView=function(a){e.views.indexOf(a)!==-1&&(e.view=a)},e.selectDate=function(a){if(g.disabled)return!1;if(o(e.date,a)&&(a=e.date),a=p(a),!a)return!1;e.date=a;var b=e.views[e.views.indexOf(e.view)+1];(!b||x||e.model)&&m(a),b?e.setView(b):D?(f.addClass("hidden"),e.$emit("hidePicker")):n()},m=function(a){a&&(e.model=a,h&&h.$setViewValue(a)),e.$emit("setDate",e.model,e.view),e.callbackOnSetDate&&e.callbackOnSetDate(g.datePicker,e.date)},e.$watch(l,k),e.watchDirectChanges&&e.$watch("model",function(){s=!1,k()}),n=function(){var a,b,f=e.view,g=e.date,h=[],i="";if(d.setParams(t,E),"date"===f){var j,k=e.weeks;for(a=0;a=f?6:-1)));for(var g=[];g.length<6&&!(a.year()===c&&a.month()>e);)g.push(this.getDaysOfWeek(a)),a.add(7,"d");return g},getVisibleYears:function(a){var c=b(a),d=c.year();c.year(d-d%10),d=c.year();for(var f,g,h=c.utcOffset()/60,i=[],j=0;j<12;j++)f=e(d,0,1,0-h),g=f.utcOffset()/60,g!==h&&(f=e(d,0,1,0-g),h=g),i.push(f),d++;return i},getDaysOfWeek:function(a){a=a?a:c?b.tz(c).day(d):b().day(d);for(var f,g,h=a.year(),i=a.month(),j=a.date(),k=[],l=a.utcOffset()/60,m=0;m<7;m++)f=e(h,i,j,0-l,0,!1),g=f.utcOffset()/60,g!==l&&(f=e(h,i,j,0-g,0,!1)),k.push(f),j++;return k},getVisibleMonths:function(a){for(var b,c,d=a.year(),f=a.utcOffset()/60,g=[],h=0;h<12;h++)b=e(d,h,1,0-f,0,!1),c=b.utcOffset()/60,c!==f&&(b=e(d,h,1,0-c,0,!1)),g.push(b);return g},getVisibleHours:function(a){var b,c,d,f=a.year(),g=a.month(),h=a.date(),i=[],j=a.utcOffset()/60;for(b=0;b<24;b++)c=e(f,g,h,b-j,0,!1),d=c.utcOffset()/60,d!==j&&(c=e(f,g,h,b-d,0,!1)),i.push(c);return i},isAfter:function(a,b){return a&&a.unix()>=b.unix()},isBefore:function(a,b){return a.unix()<=b.unix()},isSameYear:function(a,b){return a&&a.year()===b.year()},isSameMonth:function(a,b){return this.isSameYear(a,b)&&a.month()===b.month()},isSameDay:function(a,b){return this.isSameMonth(a,b)&&a.date()===b.date()},isSameHour:function(a,b){return this.isSameDay(a,b)&&a.hours()===b.hours()},isSameMinutes:function(a,b){return this.isSameHour(a,b)&&a.minutes()===b.minutes()},setParams:function(a,b){c=a,d=b},scopeSearch:function(a,b,c){var d,e,f,g=a,h=b.split("."),i=h.length;do{for(f=d=g=g.$parent,e=0;e'+f(h,m[1],"end",b.start,!1)+"
",q=c(p)(b);e.append(q)}}}]);var d="ng-pristine",e="ng-dirty",c=a.module("datePicker");c.constant("dateTimeConfig",{template:function(a,b){return"
'},format:"YYYY-MM-DD HH:mm",views:["date","year","month","hours","minutes"],autoClose:!1,position:"relative"}),c.directive("dateTimeAppend",function(){return{link:function(a,b){b.bind("click",function(){b.find("input")[0].focus()})}}}),c.directive("dateTime",["$compile","$document","$filter","dateTimeConfig","$parse","datePickerUtils",function(c,f,g,h,i,j){var k=f.find("body"),l=g("mFormat");return{require:"ngModel",scope:!0,link:function(f,g,m,n){function o(a){if(a)return l(a,x,L)}function p(a){if(!a)return"";var c=b(a,x);return c.isValid()?c:void 0}function q(a){H=a,m.minDate=a?a.format():a,I=b.isMoment(a)}function r(a){J=a,m.maxDate=a?a.format():a,K=b.isMoment(a)}function s(){w=h.template(m)}function t(a){a.stopPropagation(),n.$pristine&&(n.$dirty=!0,n.$pristine=!1,g.removeClass(d).addClass(e),y&&y.$setDirty(),n.$render())}function u(){D&&(D.remove(),D=null),G&&(G.remove(),G=null)}function v(){if(!D){if(D=c(w)(f),f.$digest(),O||(f.$on("setDate",function(a,b,c){t(a),N&&N(m.ngModel,b),C&&z[z.length-1]===c&&u()}),f.$on("hidePicker",function(){g[0].blur()}),f.$on("$destroy",u),O=!0),"absolute"===F){var b=g[0].getBoundingClientRect(),d=b.height||g[0].offsetHeight;D.css({top:b.top+d+"px",left:b.left+"px",display:"block",position:F}),k.append(D)}else G=a.element("
"),g[0].parentElement.insertBefore(G[0],g[0]),G.append(D),D.css({top:g[0].offsetHeight+"px",display:"block"});D.bind("mousedown",function(a){a.preventDefault()})}}var w,x=m.format||h.format,y=g.inheritedData("$formController"),z=i(m.views)(f)||h.views.concat(),A=m.view||z[0],B=z.indexOf(A),C=m.autoClose?i(m.autoClose)(f):h.autoClose,D=null,E=g[0].id,F=m.position||h.position,G=null,H=null,I=null,J=null,K=null,L=m.timezone||!1,M=j.eventIsForPicker,N=null,O=!1;B===-1&&z.splice(B,1),z.unshift(A),n.$formatters.push(o),n.$parsers.unshift(p),a.isDefined(m.minDate)&&(q(j.findParam(f,m.minDate)),n.$validators.min=function(a){return!I||b.isMoment(a)&&(H.isSame(a)||H.isBefore(a))}),a.isDefined(m.maxDate)&&(r(j.findParam(f,m.maxDate)),n.$validators.max=function(a){return!K||b.isMoment(a)&&(J.isSame(a)||J.isAfter(a))}),a.isDefined(m.dateChange)&&(N=j.findFunction(f,m.dateChange)),E&&f.$on("pickerUpdate",function(b,c,d){if(M(c,E))if(D);else{var e=!1;a.isDefined(d.minDate)&&(q(d.minDate),e=!0),a.isDefined(d.maxDate)&&(r(d.maxDate),e=!0),a.isDefined(d.minView)&&(m.minView=d.minView),a.isDefined(d.maxView)&&(m.maxView=d.maxView),m.view=d.view||m.view,e&&n.$validate(),a.isDefined(d.format)&&(x=m.format=d.format||h.format,n.$modelValue=-1),s()}}),g.bind("focus",v),g.bind("blur",u),s()}}}]),a.module("datePicker").run(["$templateCache",function(a){a.put("templates/datepicker.html",'
\r\n
\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n \r\n
\r\n
\r\n
\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n \r\n
\r\n
\r\n
\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n \r\n
\r\n
\r\n
\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n \r\n
\r\n
\r\n
\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n \r\n
\r\n
\r\n
')}])}); \ No newline at end of file