Skip to content

Instantly share code, notes, and snippets.

@moresheth
Last active December 19, 2015 15:09
Show Gist options
  • Save moresheth/5974678 to your computer and use it in GitHub Desktop.
Save moresheth/5974678 to your computer and use it in GitHub Desktop.
Simple datetime picker that uses native controls if available, or uses the jQuery UI picker for the date portion if not. It also uses the Resig microtemplating for the template part.
<!--
This is the Rails partial (include) that has the HTML fields.
You pass in an id_prefix and name_prefix, to keep the form elements unique.
And optionally pass in a set_date variable to have the default date.
-->
<span data-action="timechanger">
<!-- Attempt to use native date picker. -->
<input class="datefield" data-timechangerdate="true" type="date" id="<%= id_prefix %>_date" name="<%= name_prefix %>_date" value="<%= set_date.to_s(:sitemap_date) if set_date.present? %>" />
<span class="at">at</span>
<!-- Attempt to use native time picker. -->
<span class="timefield" data-timechangertimecont="tmpl_<%= id_prefix %>_timefields">
<input data-timechangertime="true" type="time" id="<%= id_prefix %>_time" name="<%= name_prefix %>_time" value="<%= set_date.to_s(:time24) if set_date.present? %>" />
</span>
<!-- Time zone display for the user, made from a Rails helper. Not clickable. -->
<span class="zone"><%= timezone_abbr %></span>
<!-- Here is the hidden input that actually gets submitted to the server. It has the time as milliseconds since epoch. -->
<input data-timechangerint="true" type="hidden" id="<%= id_prefix %>_int" name="<%= name_prefix %>_int" value="<%= set_date.to_i * 1000 %>">
</span>
<!--
This is a Javascript template for creating the time fields if the browser doesn't do native time pickers.
It could be generated from inside the Javascript plugin, and that would make the page code cleaner.
-->
<script type="text/html" id="tmpl_<%= id_prefix %>_timefields">
<select data-timechangerhour="true" id="<%= id_prefix %>_hour" name="<%= name_prefix %>_hour">
<% [12,1,2,3,4,5,6,7,8,9,10,11].each do |hour| %>
<option<%= check_selected(hour.to_s, set_date.strftime('%l').to_i.to_s) if set_date.present? %> value="<%= hour %>"><%= hour %></option>
<% end %>
</select>
:
<select data-timechangermin="true" id="<%= id_prefix %>_minute" name="<%= name_prefix %>_minute">
<% (0..59).each do |minute| %>
<option<%= check_selected(minute.to_s.rjust(2,"0"), set_date.min.to_i.to_s.rjust(2,"0")) if set_date.present? %> value="<%= minute %>"><%= minute.to_s.rjust(2,"0") %></option>
<% end %>
</select>
<select data-timechangerampm="true" id="<%= id_prefix %>_ampm" name="<%= name_prefix %>_ampm">
<% ["AM","PM"].each do |ampm| %>
<option<%= check_selected(ampm, set_date.strftime('%p').to_s.upcase) if set_date.present? %> value="<%= ampm %>"><%= ampm %></option>
<% end %>
</select>
</script>
<!-- This is how I call the Rails partial, and set the date and prefixes. -->
<div class="field">
<label for="event_start_date">Starting time</label>
<%= render :partial => "forms/datetime_picker", :locals => {:set_date => @event.display_start_at, :id_prefix => "event_start", :name_prefix => "start"} %>
</div>
<div class="field">
<label for="event_stop_date">Ending time</label>
<%= render :partial => "forms/datetime_picker", :locals => {:set_date => @event.display_stop_at, :id_prefix => "event_stop", :name_prefix => "stop"} %>
</div>
// This is a jQuery plugin that does the work.
// It uses the jQuery UI datepicker for date if the browser doesn't have a native one.
// The template with tmpl() is from a slightly modified Resig microtemplating.
// The template part could easily just be generated in this javascript, but I prefer keeping it separate.
$.fn.timechanger = function( options ) {
var defaults = {
activeClass: 'active',
hideClass: 'hideme'
};
return this.each( function() {
var element = $(this),
nativeDate = false,
nativeTime = false,
intField = element.find('[data-timechangerint]'),
dateField = element.find('[data-timechangerdate]'),
timeField = element.find('[data-timechangertime]'),
then = new Date( parseInt( intField.val(), 10 ) );
// These will be overwritten, but just following convention.
var settings = $.extend( {}, defaults, options );
// Get the settings from the data attribute.
if ( element.data('timechanger') ) $.extend( settings, element.data('timechanger') );
// First, see if the browser supports the date type.
nativeDate = checkInput('date');
// Then check the time input.
nativeTime = checkInput('time');
// If it's an older Safari, just use non-native
if ( navigator.userAgent.toLowerCase().match(/version\/[0-5]\.(\d)\.(\d) safari/i ) !== null ) {
nativeDate = false;
nativeTime = false;
}
// Then, if we are using the nativeDate, give it a change event.
if ( nativeDate ) {
dateField.change( updateInteger );
} else {
// Initialize the datepicker.
var blah = dateField.datepicker({
defaultDate: then,
dateFormat: 'yy-mm-dd',
onSelect: updateInteger
});
}
if ( nativeTime ) {
timeField.change( updateInteger );
} else {
// Time field doesn't work, so create three dropdowns.
var timeContainer = element.find('[data-timechangertimecont]');
timeContainer.html( tmpl( timeContainer.data('timechangertimecont'), {} ) );
var hourField = element.find('[data-timechangerhour]'),
minField = element.find('[data-timechangermin]'),
ampmField = element.find('[data-timechangerampm]');
// Anything else gets updated, set the integer.
element.find('select').change( updateInteger );
}
updateInteger();
function updateInteger() {
var datestr = dateField.val();
var selectedDate = new Date( datestr.split('-')[0], ( datestr.split('-')[1] - 1 ), datestr.split('-')[2] );
if ( nativeTime ) {
var timeval = timeField.val();
selectedDate.setHours( timeval.split(':')[0], timeval.split(':')[1] );
} else {
var isPm = ( ampmField.val() == "PM" );
var hours = parseInt( hourField.val(), 10 );
if ( isPm && hours < 12 ) hours += 12;
if ( !isPm && hours == 12 ) hours = 0;
var minutes = parseInt( minField.val(), 10 );
selectedDate.setHours( hours, minutes );
}
intField.val( selectedDate.getTime()).trigger('change');
}
function checkInput(type) {
var input = document.createElement('input');
input.setAttribute('type', type);
return input.type == type;
}
});
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment