/third-party/jQuery-UI-Date-Range-Picker/js/daterangepicker.jQuery.js

https://bitbucket.org/kolbyjAFK/jquery-ui-bootstrap · JavaScript · 380 lines · 283 code · 50 blank · 47 comment · 50 complexity · c9787b2bc5803c55e0130ca425bca2b8 MD5 · raw file

  1. (function ($) {
  2. /**
  3. * --------------------------------------------------------------------
  4. * jQuery-Plugin "daterangepicker.jQuery.js"
  5. * by Scott Jehl, scott@filamentgroup.com
  6. * reference article: http://www.filamentgroup.com/lab/update_date_range_picker_with_jquery_ui/
  7. * demo page: http://www.filamentgroup.com/examples/daterangepicker/
  8. *
  9. * Copyright (c) 2010 Filament Group, Inc
  10. * Dual licensed under the MIT (filamentgroup.com/examples/mit-license.txt) and GPL (filamentgroup.com/examples/gpl-license.txt) licenses.
  11. *
  12. * Dependencies: jquery, jquery UI datepicker, date.js, jQuery UI CSS Framework
  13. * 12.15.2010 Made some fixes to resolve breaking changes introduced by jQuery UI 1.8.7
  14. * --------------------------------------------------------------------
  15. */
  16. $.fn.daterangepicker = function(settings){
  17. var rangeInput = $(this);
  18. //defaults
  19. var options = $.extend({
  20. presetRanges: [
  21. {text: 'Today', dateStart: 'today', dateEnd: 'today' },
  22. {text: 'Last 7 days', dateStart: 'today-7days', dateEnd: 'today' },
  23. {text: 'Month to date', dateStart: function(){ return Date.parse('today').moveToFirstDayOfMonth(); }, dateEnd: 'today' },
  24. {text: 'Year to date', dateStart: function(){ var x= Date.parse('today'); x.setMonth(0); x.setDate(1); return x; }, dateEnd: 'today' },
  25. //extras:
  26. {text: 'The previous Month', dateStart: function(){ return Date.parse('1 month ago').moveToFirstDayOfMonth(); }, dateEnd: function(){ return Date.parse('1 month ago').moveToLastDayOfMonth(); } }
  27. //{text: 'Tomorrow', dateStart: 'Tomorrow', dateEnd: 'Tomorrow' },
  28. //{text: 'Ad Campaign', dateStart: '03/07/08', dateEnd: 'Today' },
  29. //{text: 'Last 30 Days', dateStart: 'Today-30', dateEnd: 'Today' },
  30. //{text: 'Next 30 Days', dateStart: 'Today', dateEnd: 'Today+30' },
  31. //{text: 'Our Ad Campaign', dateStart: '03/07/08', dateEnd: '07/08/08' }
  32. ],
  33. //presetRanges: array of objects for each menu preset.
  34. //Each obj must have text, dateStart, dateEnd. dateStart, dateEnd accept date.js string or a function which returns a date object
  35. presets: {
  36. specificDate: 'Specific Date',
  37. allDatesBefore: 'All Dates Before',
  38. allDatesAfter: 'All Dates After',
  39. dateRange: 'Date Range'
  40. },
  41. rangeStartTitle: 'Start date',
  42. rangeEndTitle: 'End date',
  43. nextLinkText: 'Next',
  44. prevLinkText: 'Prev',
  45. target: rangeInput,
  46. doneButtonText: 'Done',
  47. earliestDate: Date.parse('-15years'), //earliest date allowed
  48. latestDate: Date.parse('+15years'), //latest date allowed
  49. constrainDates: false,
  50. rangeSplitter: '-', //string to use between dates in single input
  51. dateFormat: 'm/d/yy', // date formatting. Available formats: http://docs.jquery.com/UI/Datepicker/%24.datepicker.formatDate
  52. closeOnSelect: true, //if a complete selection is made, close the menu
  53. arrows: false,
  54. appendTo: 'body',
  55. onClose: function(){},
  56. onOpen: function(){},
  57. onChange: function(){},
  58. datepickerOptions: null //object containing native UI datepicker API options
  59. }, settings);
  60. //custom datepicker options, extended by options
  61. var datepickerOptions = {
  62. onSelect: function(dateText, inst) {
  63. var range_start = rp.find('.range-start');
  64. var range_end = rp.find('.range-end');
  65. if(rp.find('.ui-daterangepicker-specificDate').is('.ui-state-active')){
  66. range_end.datepicker('setDate', range_start.datepicker('getDate') );
  67. }
  68. $(this).trigger('constrainOtherPicker');
  69. var rangeA = fDate( range_start.datepicker('getDate') );
  70. var rangeB = fDate( range_end.datepicker('getDate') );
  71. //send back to input or inputs
  72. if(rangeInput.length == 2){
  73. rangeInput.eq(0).val(rangeA);
  74. rangeInput.eq(1).val(rangeB);
  75. }
  76. else{
  77. rangeInput.val((rangeA != rangeB) ? rangeA+' '+ options.rangeSplitter +' '+rangeB : rangeA);
  78. }
  79. //if closeOnSelect is true
  80. if(options.closeOnSelect){
  81. if(!rp.find('li.ui-state-active').is('.ui-daterangepicker-dateRange') && !rp.is(':animated') ){
  82. hideRP();
  83. }
  84. $(this).trigger('constrainOtherPicker');
  85. options.onChange();
  86. }
  87. },
  88. defaultDate: +0
  89. };
  90. //change event fires both when a calendar is updated or a change event on the input is triggered
  91. rangeInput.bind('change', options.onChange);
  92. //datepicker options from options
  93. options.datepickerOptions = (settings) ? $.extend(datepickerOptions, settings.datepickerOptions) : datepickerOptions;
  94. //Capture Dates from input(s)
  95. var inputDateA, inputDateB = Date.parse('today');
  96. var inputDateAtemp, inputDateBtemp;
  97. if(rangeInput.size() == 2){
  98. inputDateAtemp = Date.parse( rangeInput.eq(0).val() );
  99. inputDateBtemp = Date.parse( rangeInput.eq(1).val() );
  100. if(inputDateAtemp == null){inputDateAtemp = inputDateBtemp;}
  101. if(inputDateBtemp == null){inputDateBtemp = inputDateAtemp;}
  102. }
  103. else {
  104. inputDateAtemp = Date.parse( rangeInput.val().split(options.rangeSplitter)[0] );
  105. inputDateBtemp = Date.parse( rangeInput.val().split(options.rangeSplitter)[1] );
  106. if(inputDateBtemp == null){inputDateBtemp = inputDateAtemp;} //if one date, set both
  107. }
  108. if(inputDateAtemp != null){inputDateA = inputDateAtemp;}
  109. if(inputDateBtemp != null){inputDateB = inputDateBtemp;}
  110. //build picker and
  111. var rp = $('<div class="ui-daterangepicker ui-widget ui-helper-clearfix ui-widget-content ui-corner-all"></div>');
  112. var rpPresets = (function(){
  113. var ul = $('<ul class="ui-widget-content"></ul>').appendTo(rp);
  114. $.each(options.presetRanges,function(){
  115. $('<li class="ui-daterangepicker-'+ this.text.replace(/ /g, '') +' ui-corner-all"><a href="#">'+ this.text +'</a></li>')
  116. .data('dateStart', this.dateStart)
  117. .data('dateEnd', this.dateEnd)
  118. .appendTo(ul);
  119. });
  120. var x=0;
  121. $.each(options.presets, function(key, value) {
  122. $('<li class="ui-daterangepicker-'+ key +' preset_'+ x +' ui-helper-clearfix ui-corner-all"><span class="ui-icon ui-icon-triangle-1-e"></span><a href="#">'+ value +'</a></li>')
  123. .appendTo(ul);
  124. x++;
  125. });
  126. ul.find('li').hover(
  127. function(){
  128. $(this).addClass('ui-state-hover');
  129. },
  130. function(){
  131. $(this).removeClass('ui-state-hover');
  132. })
  133. .click(function(){
  134. rp.find('.ui-state-active').removeClass('ui-state-active');
  135. $(this).addClass('ui-state-active');
  136. clickActions($(this),rp, rpPickers, doneBtn);
  137. return false;
  138. });
  139. return ul;
  140. })();
  141. //function to format a date string
  142. function fDate(date){
  143. if(!date.getDate()){return '';}
  144. var day = date.getDate();
  145. var month = date.getMonth();
  146. var year = date.getFullYear();
  147. month++; // adjust javascript month
  148. var dateFormat = options.dateFormat;
  149. return $.datepicker.formatDate( dateFormat, date );
  150. }
  151. $.fn.restoreDateFromData = function(){
  152. if($(this).data('saveDate')){
  153. $(this).datepicker('setDate', $(this).data('saveDate')).removeData('saveDate');
  154. }
  155. return this;
  156. };
  157. $.fn.saveDateToData = function(){
  158. if(!$(this).data('saveDate')){
  159. $(this).data('saveDate', $(this).datepicker('getDate') );
  160. }
  161. return this;
  162. };
  163. //show, hide, or toggle rangepicker
  164. function showRP(){
  165. if(rp.data('state') == 'closed'){
  166. positionRP();
  167. rp.fadeIn(300).data('state', 'open');
  168. options.onOpen();
  169. }
  170. }
  171. function hideRP(){
  172. if(rp.data('state') == 'open'){
  173. rp.fadeOut(300).data('state', 'closed');
  174. options.onClose();
  175. }
  176. }
  177. function toggleRP(){
  178. if( rp.data('state') == 'open' ){ hideRP(); }
  179. else { showRP(); }
  180. }
  181. function positionRP(){
  182. var relEl = riContain || rangeInput; //if arrows, use parent for offsets
  183. var riOffset = relEl.offset(),
  184. side = 'left',
  185. val = riOffset.left,
  186. offRight = $(window).width() - val - relEl.outerWidth();
  187. if(val > offRight){
  188. side = 'right', val = offRight;
  189. }
  190. rp.parent().css(side, val).css('top', riOffset.top + relEl.outerHeight());
  191. }
  192. //preset menu click events
  193. function clickActions(el, rp, rpPickers, doneBtn){
  194. if(el.is('.ui-daterangepicker-specificDate')){
  195. //Specific Date (show the "start" calendar)
  196. doneBtn.hide();
  197. rpPickers.show();
  198. rp.find('.title-start').text( options.presets.specificDate );
  199. rp.find('.range-start').restoreDateFromData().css('opacity',1).show(400);
  200. rp.find('.range-end').restoreDateFromData().css('opacity',0).hide(400);
  201. setTimeout(function(){doneBtn.fadeIn();}, 400);
  202. }
  203. else if(el.is('.ui-daterangepicker-allDatesBefore')){
  204. //All dates before specific date (show the "end" calendar and set the "start" calendar to the earliest date)
  205. doneBtn.hide();
  206. rpPickers.show();
  207. rp.find('.title-end').text( options.presets.allDatesBefore );
  208. rp.find('.range-start').saveDateToData().datepicker('setDate', options.earliestDate).css('opacity',0).hide(400);
  209. rp.find('.range-end').restoreDateFromData().css('opacity',1).show(400);
  210. setTimeout(function(){doneBtn.fadeIn();}, 400);
  211. }
  212. else if(el.is('.ui-daterangepicker-allDatesAfter')){
  213. //All dates after specific date (show the "start" calendar and set the "end" calendar to the latest date)
  214. doneBtn.hide();
  215. rpPickers.show();
  216. rp.find('.title-start').text( options.presets.allDatesAfter );
  217. rp.find('.range-start').restoreDateFromData().css('opacity',1).show(400);
  218. rp.find('.range-end').saveDateToData().datepicker('setDate', options.latestDate).css('opacity',0).hide(400);
  219. setTimeout(function(){doneBtn.fadeIn();}, 400);
  220. }
  221. else if(el.is('.ui-daterangepicker-dateRange')){
  222. //Specific Date range (show both calendars)
  223. doneBtn.hide();
  224. rpPickers.show();
  225. rp.find('.title-start').text(options.rangeStartTitle);
  226. rp.find('.title-end').text(options.rangeEndTitle);
  227. rp.find('.range-start').restoreDateFromData().css('opacity',1).show(400);
  228. rp.find('.range-end').restoreDateFromData().css('opacity',1).show(400);
  229. setTimeout(function(){doneBtn.fadeIn();}, 400);
  230. }
  231. else {
  232. //custom date range specified in the options (no calendars shown)
  233. doneBtn.hide();
  234. rp.find('.range-start, .range-end').css('opacity',0).hide(400, function(){
  235. rpPickers.hide();
  236. });
  237. var dateStart = (typeof el.data('dateStart') == 'string') ? Date.parse(el.data('dateStart')) : el.data('dateStart')();
  238. var dateEnd = (typeof el.data('dateEnd') == 'string') ? Date.parse(el.data('dateEnd')) : el.data('dateEnd')();
  239. rp.find('.range-start').datepicker('setDate', dateStart).find('.ui-datepicker-current-day').trigger('click');
  240. rp.find('.range-end').datepicker('setDate', dateEnd).find('.ui-datepicker-current-day').trigger('click');
  241. }
  242. return false;
  243. }
  244. //picker divs
  245. var rpPickers = $('<div class="ranges ui-widget-header ui-corner-all ui-helper-clearfix"><div class="range-start"><span class="title-start">Start Date</span></div><div class="range-end"><span class="title-end">End Date</span></div></div>').appendTo(rp);
  246. rpPickers.find('.range-start, .range-end')
  247. .datepicker(options.datepickerOptions);
  248. rpPickers.find('.range-start').datepicker('setDate', inputDateA);
  249. rpPickers.find('.range-end').datepicker('setDate', inputDateB);
  250. rpPickers.find('.range-start, .range-end')
  251. .bind('constrainOtherPicker', function(){
  252. if(options.constrainDates){
  253. //constrain dates
  254. if($(this).is('.range-start')){
  255. rp.find('.range-end').datepicker( "option", "minDate", $(this).datepicker('getDate'));
  256. }
  257. else{
  258. rp.find('.range-start').datepicker( "option", "maxDate", $(this).datepicker('getDate'));
  259. }
  260. }
  261. })
  262. .trigger('constrainOtherPicker');
  263. var doneBtn = $('<button class="btnDone ui-state-default ui-corner-all">'+ options.doneButtonText +'</button>')
  264. .click(function(){
  265. rp.find('.ui-datepicker-current-day').trigger('click');
  266. hideRP();
  267. })
  268. .hover(
  269. function(){
  270. $(this).addClass('ui-state-hover');
  271. },
  272. function(){
  273. $(this).removeClass('ui-state-hover');
  274. }
  275. )
  276. .appendTo(rpPickers);
  277. //inputs toggle rangepicker visibility
  278. $(this).click(function(){
  279. toggleRP();
  280. return false;
  281. });
  282. //hide em all
  283. rpPickers.hide().find('.range-start, .range-end, .btnDone').hide();
  284. rp.data('state', 'closed');
  285. //Fixed for jQuery UI 1.8.7 - Calendars are hidden otherwise!
  286. rpPickers.find('.ui-datepicker').css("display","block");
  287. //inject rp
  288. $(options.appendTo).append(rp);
  289. //wrap and position
  290. rp.wrap('<div class="ui-daterangepickercontain"></div>');
  291. //add arrows (only available on one input)
  292. if(options.arrows && rangeInput.size()==1){
  293. var prevLink = $('<a href="#" class="ui-daterangepicker-prev ui-corner-all" title="'+ options.prevLinkText +'"><span class="ui-icon ui-icon-circle-triangle-w">'+ options.prevLinkText +'</span></a>');
  294. var nextLink = $('<a href="#" class="ui-daterangepicker-next ui-corner-all" title="'+ options.nextLinkText +'"><span class="ui-icon ui-icon-circle-triangle-e">'+ options.nextLinkText +'</span></a>');
  295. $(this)
  296. .addClass('ui-rangepicker-input ui-widget-content')
  297. .wrap('<div class="ui-daterangepicker-arrows ui-widget ui-widget-header ui-helper-clearfix ui-corner-all"></div>')
  298. .before( prevLink )
  299. .before( nextLink )
  300. .parent().find('a').click(function(){
  301. var dateA = rpPickers.find('.range-start').datepicker('getDate');
  302. var dateB = rpPickers.find('.range-end').datepicker('getDate');
  303. var diff = Math.abs( new TimeSpan(dateA - dateB).getTotalMilliseconds() ) + 86400000; //difference plus one day
  304. if($(this).is('.ui-daterangepicker-prev')){ diff = -diff; }
  305. rpPickers.find('.range-start, .range-end ').each(function(){
  306. var thisDate = $(this).datepicker( "getDate");
  307. if(thisDate == null){return false;}
  308. $(this).datepicker( "setDate", thisDate.add({milliseconds: diff}) ).find('.ui-datepicker-current-day').trigger('click');
  309. });
  310. return false;
  311. })
  312. .hover(
  313. function(){
  314. $(this).addClass('ui-state-hover');
  315. },
  316. function(){
  317. $(this).removeClass('ui-state-hover');
  318. });
  319. var riContain = rangeInput.parent();
  320. }
  321. $(document).click(function(){
  322. if (rp.is(':visible')) {
  323. hideRP();
  324. }
  325. });
  326. rp.click(function(){return false;}).hide();
  327. return this;
  328. }
  329. })(jQuery);