maps.nyc.gov

Source: collapsible-month-range.js

var nyc = nyc || {};

/**
 * @desc A UI class to pick a month range
 * @public
 * @class
 * @extends {nyc.Collapsible}
 * @constructor
 * @param {nyc.MonthRangePicker.Options} options Constructor options
 */
nyc.MonthRangePicker = function(options){
	var labelMin = $('<label>The beginning of</label>'), 
		labelMax = $('<label>through the end of</label>');
	
	this.minDate = this.firstOfMonth(options.minMonth, options.minYear);
	this.maxDate = this.lastOfMonth(options.maxMonth, options.maxYear);

	this.minDates = [];
	this.maxDates = [];

	this.min = $('<select class="date-min"></select>').uniqueId();
	this.max = $('<select class="date-max"></select>').uniqueId();
	labelMin.attr('for', this.min.attr('id'));
	labelMax.attr('for', this.max.attr('id'));
	
	$(options.target).append(labelMin)
		.append(this.min)
		.append(labelMax)
		.append(this.max);

	nyc.Collapsible.apply(this, [options]);
	
	this.populate(options);
	
	this.min.get(0).selectedIndex = this.min.children().length - 1;
	this.max.get(0).selectedIndex = this.max.children().length - 1;
	this.min.selectmenu().selectmenu('refresh', true);
	this.max.selectmenu().selectmenu('refresh', true);

	$(this.min).change($.proxy(this.disableMax, this));
	$(this.max).change($.proxy(this.disableMin, this));
	this.changed();
	this.disableMax();
};

nyc.MonthRangePicker.prototype = {
	/** 
	 * @desc The minimum date
	 * @public 
	 * @member {Date}
	 */
	minDate: null,
	/** 
	 * @desc The maximum date
	 * @public 
	 * @member {Date}
	 */
	maxDate: null,
	/** 
	 * @private 
	 * @member {Array<Date>}
	 */
	minDates: null,
	/** 
	 * @private 
	 * @member {Array<Date>}
	 */
	maxDates: null,
	/** 
	 * @private 
	 * @member {JQuery}
	 */
	min: null,
	/** 
	 * @private 
	 * @member {JQuery}
	 */
	max: null,
	/** 
	 * @private 
	 * @member {Date}
	 */
	value: null,
	/** 
	 * @private 
	 * @member {Array<string>}
	 */
	months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
	/** 
	 * @private
	 * @method
	 * @param {nyc.MonthRangePicker.Options} options
	 */
	populate: function(options){
		for (var year = options.minYear; year <= options.maxYear; year++){
			for (var month = 0; month < 12; month++){		
				var min = this.firstOfMonth(month, year), max = this.lastOfMonth(month, year);
				if (min >= this.minDate && min <= this.maxDate){
					this.minDates.push(min);
					this.appendOpt(this.min, min);						
				}
				if (max >= this.minDate && max <= this.maxDate){
					this.maxDates.push(max);
					this.appendOpt(this.max, max);
				}
			}
		}
	},
	/** 
	 * @private
	 * @method
	 * @param {JQuery} parent
	 * @param {Date} date
	 */
	appendOpt: function(parent, date){
		var opt = $('<option></option>');
		opt.html(this.months[date.getMonth()] + ' ' + date.getFullYear());
		opt.attr('value', date.toISOString());
		parent.append(opt);
	},
	/** 
	 * @private
	 * @method
	 * @param {number} month
	 * @param {number} year
	 * @return {Date}
	 */
	firstOfMonth: function(month, year){
		return this.localeDate(year + '-' + this.pad(month + 1) + '-01');		
	},
	/** 
	 * @private
	 * @method
	 * @param {number} month
	 * @param {number} year
	 * @return {Date}
	 */
	lastOfMonth: function(month, year){
		var date = new Date(year, month + 1, 0).getDate();  
		return this.localeDate(year + '-' + this.pad(month + 1) + '-' + date);		
	},
	/** 
	 * @private
	 * @method
	 * @param {string} dateString
	 * @return {Date}
	 */
	localeDate: function(dateString){
		var utcDate = dateString ? new Date(dateString) : new Date();
		return new Date(utcDate.getTime() + (utcDate.getTimezoneOffset() * 60000));
	},
	/** 
	 * @private
	 * @method
	 * @param {number} number
	 * @return {string}
	 */
	pad: function(number){
		return (number < 10 ? '0' : '') + number;
	},
	/** 
	 * @private
	 * @method	
	 */
	disableMax: function(){
		var me = this, minDate = new Date(this.min.val());
		$.each(me.maxDates, function(i, date){
			$(me.max.children().get(i))[date < minDate ? 'hide' : 'show']();
		});
		this.changed();
	},
	/** 
	 * @private
	 * @method	
	 */
	disableMin: function(){
		var me = this, maxDate = new Date(this.max.val());		
		$.each(me.minDates, function(i, date){
			$(me.min.children().get(i))[date > maxDate ? 'hide' : 'show']();
		});
		this.changed();
	},
	/** 
	 * @private
	 * @method	
 	 */
	changed: function(){
		var start = new Date(this.min.val()), end = new Date(this.max.val());
		this.currentVal.html(start.toLocaleDateString() + ' - ' + end.toLocaleDateString());
		this.value = {start: start, end: end};
		this.trigger('change', this.value);
	},
	/** 
	 * @desc Returns the date range value
	 * @public
	 * @method	
	 * @return {nyc.MonthRangePicker.DateRange} The date range
	 */
	val: function(){
		return this.value;
	}
};

nyc.inherits(nyc.MonthRangePicker, nyc.Collapsible);

/**
 * @desc Date range object
 * @public
 * @typedef {Object}
 * @property {Date} start The start of the range
 * @property {Date} end The end of the range
 */
nyc.MonthRangePicker.DateRange;

/**
 * @desc Constructor options for {@link nyc.MonthRangePicker}
 * @public
 * @typedef {Object}
 * @property {number} minMonth The zero-based index of the minimum month
 * @property {number} minYear The 4 digit minimum year
 * @property {number} maxMonth The zero-based index of the maximum month
 * @property {number} maxYear The 4 digit maximum year
 * @property {(String|Element|JQuery)} target The DOM target
 */
nyc.MonthRangePicker.Options;